From 7a5fa7f12ec439ef5a4af29aa86498f799b6b9a5 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 6 Feb 2007 21:05:34 +0000 Subject: r21191: Add in the POSIX open/mkdir/unlink calls. Move more error code returns to NTSTATUS. Client test code to follow... See if this passes the build-farm before I add it into 3.0.25. Jeremy. (This used to be commit 83dbbdff345fa9e427c9579183f4380004bf3dd7) --- source3/smbd/close.c | 111 +++++++++--------- source3/smbd/dir.c | 1 + source3/smbd/fake_file.c | 4 +- source3/smbd/nttrans.c | 26 +++-- source3/smbd/open.c | 109 ++++++++++++------ source3/smbd/oplock.c | 2 + source3/smbd/reply.c | 54 ++++----- source3/smbd/trans2.c | 290 ++++++++++++++++++++++++++++++++++++++++++++++- 8 files changed, 460 insertions(+), 137 deletions(-) (limited to 'source3/smbd') diff --git a/source3/smbd/close.c b/source3/smbd/close.c index 714e0615de..05a45cc14f 100644 --- a/source3/smbd/close.c +++ b/source3/smbd/close.c @@ -2,7 +2,7 @@ Unix SMB/CIFS implementation. file closing Copyright (C) Andrew Tridgell 1992-1998 - Copyright (C) Jeremy Allison 1992-2004. + Copyright (C) Jeremy Allison 1992-2007. Copyright (C) Volker Lendecke 2005 This program is free software; you can redistribute it and/or modify @@ -90,22 +90,21 @@ static void check_magic(files_struct *fsp,connection_struct *conn) Common code to close a file or a directory. ****************************************************************************/ -static int close_filestruct(files_struct *fsp) +static NTSTATUS close_filestruct(files_struct *fsp) { + NTSTATUS status = NT_STATUS_OK; connection_struct *conn = fsp->conn; - int ret = 0; if (fsp->fh->fd != -1) { - if(flush_write_cache(fsp, CLOSE_FLUSH) == -1) - ret = -1; - + if(flush_write_cache(fsp, CLOSE_FLUSH) == -1) { + status = map_nt_error_from_unix(errno); + } delete_write_cache(fsp); } conn->num_files_open--; SAFE_FREE(fsp->wbmpx_ptr); - - return ret; + return status; } /**************************************************************************** @@ -195,9 +194,13 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp, if (delete_file) { int i; /* See if others still have the file open. If this is the - * case, then don't delete */ + * case, then don't delete. If all opens are POSIX delete now. */ for (i=0; inum_share_modes; i++) { - if (is_valid_share_mode_entry(&lck->share_modes[i])) { + struct share_mode_entry *e = &lck->share_modes[i]; + if (is_valid_share_mode_entry(e)) { + if (fsp->posix_open && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) { + continue; + } delete_file = False; break; } @@ -282,12 +285,10 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp, fsp->fsp_name, strerror(errno) )); status = map_nt_error_from_unix(errno); - goto done; } - status = NT_STATUS_FILE_DELETED; - done: + /* unbecome user. */ pop_sec_ctx(); @@ -303,12 +304,13 @@ static NTSTATUS close_remove_share_mode(files_struct *fsp, delete on close is done on normal and shutdown close. ****************************************************************************/ -static int close_normal_file(files_struct *fsp, enum file_close_type close_type) +static NTSTATUS close_normal_file(files_struct *fsp, enum file_close_type close_type) { + NTSTATUS status = NT_STATUS_OK; + NTSTATUS saved_status1 = NT_STATUS_OK; + NTSTATUS saved_status2 = NT_STATUS_OK; + NTSTATUS saved_status3 = NT_STATUS_OK; connection_struct *conn = fsp->conn; - int saved_errno = 0; - int err = 0; - int err1 = 0; if (fsp->aio_write_behind) { /* @@ -317,8 +319,7 @@ static int close_normal_file(files_struct *fsp, enum file_close_type close_type) */ int ret = wait_for_aio_completion(fsp); if (ret) { - saved_errno = ret; - err1 = -1; + saved_status1 = map_nt_error_from_unix(ret); } } else { cancel_aio_by_fsp(fsp); @@ -329,15 +330,12 @@ static int close_normal_file(files_struct *fsp, enum file_close_type close_type) * error here, we must remember this. */ - if (close_filestruct(fsp) == -1) { - saved_errno = errno; - err1 = -1; - } + saved_status2 = close_filestruct(fsp); if (fsp->print_file) { print_fsp_end(fsp, close_type); file_free(fsp); - return 0; + return NT_STATUS_OK; } /* If this is an old DOS or FCB open and we have multiple opens on @@ -346,7 +344,7 @@ static int close_normal_file(files_struct *fsp, enum file_close_type close_type) if (fsp->fh->ref_count == 1) { /* Should we return on error here... ? */ - close_remove_share_mode(fsp, close_type); + saved_status3 = close_remove_share_mode(fsp, close_type); } if(fsp->oplock_type) { @@ -355,13 +353,7 @@ static int close_normal_file(files_struct *fsp, enum file_close_type close_type) locking_close_file(fsp); - err = fd_close(conn, fsp); - - /* Only save errno if fd_close failed and we don't already - have an errno saved from a flush call. */ - if ((err1 != -1) && (err == -1)) { - saved_errno = errno; - } + status = fd_close(conn, fsp); /* check for magic scripts */ if (close_type == NORMAL_CLOSE) { @@ -378,29 +370,34 @@ static int close_normal_file(files_struct *fsp, enum file_close_type close_type) set_filetime(conn, fsp->fsp_name, fsp->last_write_time); } + if (NT_STATUS_IS_OK(status)) { + if (!NT_STATUS_IS_OK(saved_status1)) { + status = saved_status1; + } else if (!NT_STATUS_IS_OK(saved_status2)) { + status = saved_status2; + } else if (!NT_STATUS_IS_OK(saved_status3)) { + status = saved_status3; + } + } + DEBUG(2,("%s closed file %s (numopen=%d) %s\n", conn->user,fsp->fsp_name, conn->num_files_open, - (err == -1 || err1 == -1) ? strerror(saved_errno) : "")); + nt_errstr(status) )); file_free(fsp); - - if (err == -1 || err1 == -1) { - errno = saved_errno; - return saved_errno; - } else { - return 0; - } + return status; } /**************************************************************************** Close a directory opened by an NT SMB call. ****************************************************************************/ -static int close_directory(files_struct *fsp, enum file_close_type close_type) +static NTSTATUS close_directory(files_struct *fsp, enum file_close_type close_type) { struct share_mode_lock *lck = 0; BOOL delete_dir = False; + NTSTATUS status = NT_STATUS_OK; /* * NT can set delete_on_close of the last open @@ -411,7 +408,7 @@ static int close_directory(files_struct *fsp, enum file_close_type close_type) if (lck == NULL) { DEBUG(0, ("close_directory: Could not get share mode lock for %s\n", fsp->fsp_name)); - return EINVAL; + return NT_STATUS_INVALID_PARAMETER; } if (!del_share_mode(lck, fsp)) { @@ -441,9 +438,13 @@ static int close_directory(files_struct *fsp, enum file_close_type close_type) if (delete_dir) { int i; /* See if others still have the dir open. If this is the - * case, then don't delete */ + * case, then don't delete. If all opens are POSIX delete now. */ for (i=0; inum_share_modes; i++) { - if (is_valid_share_mode_entry(&lck->share_modes[i])) { + struct share_mode_entry *e = &lck->share_modes[i]; + if (is_valid_share_mode_entry(e)) { + if (fsp->posix_open && (e->flags & SHARE_MODE_FLAG_POSIX_OPEN)) { + continue; + } delete_dir = False; break; } @@ -469,7 +470,7 @@ static int close_directory(files_struct *fsp, enum file_close_type close_type) TALLOC_FREE(lck); - ok = rmdir_internals(fsp->conn, fsp->fsp_name); + status = rmdir_internals(fsp->conn, fsp->fsp_name); DEBUG(5,("close_directory: %s. Delete on close was set - deleting directory %s.\n", fsp->fsp_name, ok ? "succeeded" : "failed" )); @@ -482,7 +483,7 @@ static int close_directory(files_struct *fsp, enum file_close_type close_type) * now fail as the directory has been deleted. */ - if(ok) { + if(NT_STATUS_IS_OK(status)) { remove_pending_change_notify_requests_by_fid(fsp, NT_STATUS_DELETE_PENDING); } } else { @@ -496,35 +497,35 @@ static int close_directory(files_struct *fsp, enum file_close_type close_type) */ close_filestruct(fsp); file_free(fsp); - return 0; + return status; } /**************************************************************************** Close a 'stat file' opened internally. ****************************************************************************/ -static int close_stat(files_struct *fsp) +NTSTATUS close_stat(files_struct *fsp) { /* * Do the code common to files and directories. */ close_filestruct(fsp); file_free(fsp); - return 0; + return NT_STATUS_OK; } /**************************************************************************** Close a files_struct. ****************************************************************************/ -int close_file(files_struct *fsp, enum file_close_type close_type) +NTSTATUS close_file(files_struct *fsp, enum file_close_type close_type) { - if(fsp->is_directory) + if(fsp->is_directory) { return close_directory(fsp, close_type); - else if (fsp->is_stat) + } else if (fsp->is_stat) { return close_stat(fsp); - else if (fsp->fake_file_handle != NULL) + } else if (fsp->fake_file_handle != NULL) { return close_fake_file(fsp); - else - return close_normal_file(fsp, close_type); + } + return close_normal_file(fsp, close_type); } diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c index 98356882aa..9f0350fe6d 100644 --- a/source3/smbd/dir.c +++ b/source3/smbd/dir.c @@ -884,6 +884,7 @@ static BOOL user_can_read_file(connection_struct *conn, char *name, SMB_STRUCT_S FILE_SHARE_READ|FILE_SHARE_WRITE, FILE_OPEN, 0, /* no create options. */ + FILE_ATTRIBUTE_DIRECTORY, NULL, &fsp); } else { status = open_file_stat(conn, name, pst, &fsp); diff --git a/source3/smbd/fake_file.c b/source3/smbd/fake_file.c index 7c5eeae5c7..208b325667 100644 --- a/source3/smbd/fake_file.c +++ b/source3/smbd/fake_file.c @@ -160,8 +160,8 @@ void destroy_fake_file_handle(FAKE_FILE_HANDLE **fh) (*fh) = NULL; } -int close_fake_file(files_struct *fsp) +NTSTATUS close_fake_file(files_struct *fsp) { file_free(fsp); - return 0; + return NT_STATUS_OK; } diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c index 543f393c89..19710d1dcd 100644 --- a/source3/smbd/nttrans.c +++ b/source3/smbd/nttrans.c @@ -281,10 +281,10 @@ static BOOL saved_short_case_preserve; Save case semantics. ****************************************************************************/ -static void set_posix_case_semantics(connection_struct *conn, uint32 file_attributes) +static uint32 set_posix_case_semantics(connection_struct *conn, uint32 file_attributes) { if(!(file_attributes & FILE_FLAG_POSIX_SEMANTICS)) { - return; + return file_attributes; } saved_case_sensitive = conn->case_sensitive; @@ -295,6 +295,8 @@ static void set_posix_case_semantics(connection_struct *conn, uint32 file_attrib conn->case_sensitive = True; conn->case_preserve = True; conn->short_case_preserve = True; + + return (file_attributes & ~FILE_FLAG_POSIX_SEMANTICS); } /**************************************************************************** @@ -455,6 +457,7 @@ int reply_ntcreate_and_X(connection_struct *conn, uint32 flags = IVAL(inbuf,smb_ntcreate_Flags); uint32 access_mask = IVAL(inbuf,smb_ntcreate_DesiredAccess); uint32 file_attributes = IVAL(inbuf,smb_ntcreate_FileAttributes); + uint32 new_file_attributes; uint32 share_access = IVAL(inbuf,smb_ntcreate_ShareAccess); uint32 create_disposition = IVAL(inbuf,smb_ntcreate_CreateDisposition); uint32 create_options = IVAL(inbuf,smb_ntcreate_CreateOptions); @@ -625,7 +628,7 @@ int reply_ntcreate_and_X(connection_struct *conn, * Check if POSIX semantics are wanted. */ - set_posix_case_semantics(conn, file_attributes); + new_file_attributes = set_posix_case_semantics(conn, file_attributes); status = unix_convert(conn, fname, False, NULL, &sbuf); if (!NT_STATUS_IS_OK(status)) { @@ -679,6 +682,7 @@ int reply_ntcreate_and_X(connection_struct *conn, share_access, create_disposition, create_options, + new_file_attributes, &info, &fsp); restore_case_semantics(conn, file_attributes); @@ -714,7 +718,7 @@ int reply_ntcreate_and_X(connection_struct *conn, share_access, create_disposition, create_options, - file_attributes, + new_file_attributes, oplock_request, &info, &fsp); if (!NT_STATUS_IS_OK(status)) { @@ -756,6 +760,7 @@ int reply_ntcreate_and_X(connection_struct *conn, share_access, create_disposition, create_options, + new_file_attributes, &info, &fsp); if(!NT_STATUS_IS_OK(status)) { @@ -1096,6 +1101,7 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o uint32 flags; uint32 access_mask; uint32 file_attributes; + uint32 new_file_attributes; uint32 share_access; uint32 create_disposition; uint32 create_options; @@ -1252,7 +1258,7 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o * Check if POSIX semantics are wanted. */ - set_posix_case_semantics(conn, file_attributes); + new_file_attributes = set_posix_case_semantics(conn, file_attributes); RESOLVE_DFSPATH(fname, conn, inbuf, outbuf); @@ -1324,6 +1330,7 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o share_access, create_disposition, create_options, + new_file_attributes, &info, &fsp); if(!NT_STATUS_IS_OK(status)) { restore_case_semantics(conn, file_attributes); @@ -1341,7 +1348,7 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o share_access, create_disposition, create_options, - file_attributes, + new_file_attributes, oplock_request, &info, &fsp); @@ -1364,6 +1371,7 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o share_access, create_disposition, create_options, + new_file_attributes, &info, &fsp); if(!NT_STATUS_IS_OK(status)) { restore_case_semantics(conn, file_attributes); @@ -1570,7 +1578,6 @@ static NTSTATUS copy_internals(connection_struct *conn, char *oldname, char *new uint32 fattr; int info; SMB_OFF_T ret=-1; - int close_ret; NTSTATUS status = NT_STATUS_OK; ZERO_STRUCT(sbuf1); @@ -1670,7 +1677,7 @@ static NTSTATUS copy_internals(connection_struct *conn, char *oldname, char *new /* Ensure the modtime is set correctly on the destination file. */ fsp_set_pending_modtime(fsp2, sbuf1.st_mtime); - close_ret = close_file(fsp2,NORMAL_CLOSE); + status = close_file(fsp2,NORMAL_CLOSE); /* Grrr. We have to do this as open_file_ntcreate adds aARCH when it creates the file. This isn't the correct thing to do in the copy @@ -1682,8 +1689,7 @@ static NTSTATUS copy_internals(connection_struct *conn, char *oldname, char *new return NT_STATUS_DISK_FULL; } - if (close_ret != 0) { - status = map_nt_error_from_unix(close_ret); + if (!NT_STATUS_IS_OK(status)) { DEBUG(3,("copy_internals: Error %s copy file %s to %s\n", nt_errstr(status), oldname, newname)); } diff --git a/source3/smbd/open.c b/source3/smbd/open.c index 2415433fe9..c9e29cf0e5 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -67,14 +67,13 @@ static BOOL fd_open(struct connection_struct *conn, Close the file associated with a fsp. ****************************************************************************/ -int fd_close(struct connection_struct *conn, - files_struct *fsp) +NTSTATUS fd_close(struct connection_struct *conn, files_struct *fsp) { if (fsp->fh->fd == -1) { - return 0; /* What we used to call a stat open. */ + return NT_STATUS_OK; /* What we used to call a stat open. */ } if (fsp->fh->ref_count > 1) { - return 0; /* Shared handle. Only close last reference. */ + return NT_STATUS_OK; /* Shared handle. Only close last reference. */ } return fd_close_posix(conn, fsp); } @@ -1118,6 +1117,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, int flags2=0; BOOL file_existed = VALID_STAT(*psbuf); BOOL def_acl = False; + BOOL posix_open = False; SMB_DEV_T dev = 0; SMB_INO_T inode = 0; NTSTATUS fsp_open = NT_STATUS_ACCESS_DENIED; @@ -1156,10 +1156,16 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, return NT_STATUS_NO_MEMORY; } - /* We add aARCH to this as this mode is only used if the file is - * created new. */ - unx_mode = unix_mode(conn, new_dos_attributes | aARCH, fname, - parent_dir); + if (new_dos_attributes & FILE_FLAG_POSIX_SEMANTICS) { + posix_open = True; + unx_mode = (mode_t)(new_dos_attributes & ~FILE_FLAG_POSIX_SEMANTICS); + new_dos_attributes = 0; + } else { + /* We add aARCH to this as this mode is only used if the file is + * created new. */ + unx_mode = unix_mode(conn, new_dos_attributes | aARCH, fname, + parent_dir); + } DEBUG(10, ("open_file_ntcreate: fname=%s, dos_attrs=0x%x " "access_mask=0x%x share_access=0x%x " @@ -1197,9 +1203,11 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, return status; } - new_dos_attributes &= SAMBA_ATTRIBUTES_MASK; - if (file_existed) { - existing_dos_attributes = dos_mode(conn, fname, psbuf); + if (!posix_open) { + new_dos_attributes &= SAMBA_ATTRIBUTES_MASK; + if (file_existed) { + existing_dos_attributes = dos_mode(conn, fname, psbuf); + } } /* ignore any oplock requests if oplocks are disabled */ @@ -1294,7 +1302,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, /* We only care about matching attributes on file exists and * overwrite. */ - if (file_existed && ((create_disposition == FILE_OVERWRITE) || + if (!posix_open && file_existed && ((create_disposition == FILE_OVERWRITE) || (create_disposition == FILE_OVERWRITE_IF))) { if (!open_match_attributes(conn, fname, existing_dos_attributes, @@ -1359,7 +1367,11 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, } #endif /* O_SYNC */ - if (!CAN_WRITE(conn)) { + if (posix_open & (access_mask & FILE_APPEND_DATA)) { + flags2 |= O_APPEND; + } + + if (!posix_open && !CAN_WRITE(conn)) { /* * We should really return a permission denied error if either * O_CREAT or O_TRUNC are set, but for compatibility with @@ -1393,6 +1405,8 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, fsp->access_mask = open_access_mask; /* We change this to the * requested access_mask after * the open is done. */ + fsp->posix_open = posix_open; + /* Ensure no SAMBA_PRIVATE bits can be set. */ fsp->oplock_type = (oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK); @@ -1769,9 +1783,11 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, /* Files should be initially set as archive */ if (lp_map_archive(SNUM(conn)) || lp_store_dos_attributes(SNUM(conn))) { - file_set_dosmode(conn, fname, + if (!posix_open) { + file_set_dosmode(conn, fname, new_dos_attributes | aARCH, NULL, parent_dir); + } } } @@ -1780,7 +1796,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, * selected. */ - if (!file_existed && !def_acl) { + if (!posix_open && !file_existed && !def_acl) { int saved_errno = errno; /* We might get ENOSYS in the next * call.. */ @@ -1873,15 +1889,17 @@ NTSTATUS open_file_fchmod(connection_struct *conn, const char *fname, Close the fchmod file fd - ensure no locks are lost. ****************************************************************************/ -int close_file_fchmod(files_struct *fsp) +NTSTATUS close_file_fchmod(files_struct *fsp) { - int ret = fd_close(fsp->conn, fsp); + NTSTATUS status = fd_close(fsp->conn, fsp); file_free(fsp); - return ret; + return status; } -static NTSTATUS mkdir_internal(connection_struct *conn, const char *name, - SMB_STRUCT_STAT *psbuf) +static NTSTATUS mkdir_internal(connection_struct *conn, + const char *name, + uint32 file_attributes, + SMB_STRUCT_STAT *psbuf) { int ret= -1; mode_t mode; @@ -1905,7 +1923,11 @@ static NTSTATUS mkdir_internal(connection_struct *conn, const char *name, return NT_STATUS_NO_MEMORY; } - mode = unix_mode(conn, aDIR, name, parent_dir); + if (file_attributes & FILE_FLAG_POSIX_SEMANTICS) { + mode = (mode_t)(file_attributes & ~FILE_FLAG_POSIX_SEMANTICS); + } else { + mode = unix_mode(conn, aDIR, name, parent_dir); + } if ((ret=SMB_VFS_MKDIR(conn, name, mode)) != 0) { return map_nt_error_from_unix(errno); @@ -1930,15 +1952,17 @@ static NTSTATUS mkdir_internal(connection_struct *conn, const char *name, inherit_access_acl(conn, parent_dir, name, mode); } - /* - * Check if high bits should have been set, - * then (if bits are missing): add them. - * Consider bits automagically set by UNIX, i.e. SGID bit from parent - * dir. - */ - if (mode & ~(S_IRWXU|S_IRWXG|S_IRWXO) && (mode & ~psbuf->st_mode)) { - SMB_VFS_CHMOD(conn, name, - psbuf->st_mode | (mode & ~psbuf->st_mode)); + if (!(file_attributes & FILE_FLAG_POSIX_SEMANTICS)) { + /* + * Check if high bits should have been set, + * then (if bits are missing): add them. + * Consider bits automagically set by UNIX, i.e. SGID bit from parent + * dir. + */ + if (mode & ~(S_IRWXU|S_IRWXG|S_IRWXO) && (mode & ~psbuf->st_mode)) { + SMB_VFS_CHMOD(conn, name, + psbuf->st_mode | (mode & ~psbuf->st_mode)); + } } /* Change the owner if required. */ @@ -1963,6 +1987,7 @@ NTSTATUS open_directory(connection_struct *conn, uint32 share_access, uint32 create_disposition, uint32 create_options, + uint32 file_attributes, int *pinfo, files_struct **result) { @@ -1974,12 +1999,13 @@ NTSTATUS open_directory(connection_struct *conn, DEBUG(5,("open_directory: opening directory %s, access_mask = 0x%x, " "share_access = 0x%x create_options = 0x%x, " - "create_disposition = 0x%x\n", + "create_disposition = 0x%x, file_attributes = 0x%x\n", fname, (unsigned int)access_mask, (unsigned int)share_access, (unsigned int)create_options, - (unsigned int)create_disposition)); + (unsigned int)create_disposition, + (unsigned int)file_attributes)); if (is_ntfs_stream_name(fname)) { DEBUG(0,("open_directory: %s is a stream name!\n", fname )); @@ -2006,7 +2032,11 @@ NTSTATUS open_directory(connection_struct *conn, /* If directory exists error. If directory doesn't * exist create. */ - status = mkdir_internal(conn, fname, psbuf); + status = mkdir_internal(conn, + fname, + file_attributes, + psbuf); + if (!NT_STATUS_IS_OK(status)) { DEBUG(2, ("open_directory: unable to create " "%s. Error was %s\n", fname, @@ -2023,7 +2053,10 @@ NTSTATUS open_directory(connection_struct *conn, * exist create. */ - status = mkdir_internal(conn, fname, psbuf); + status = mkdir_internal(conn, + fname, + file_attributes, + psbuf); if (NT_STATUS_IS_OK(status)) { info = FILE_WAS_CREATED; @@ -2081,6 +2114,8 @@ NTSTATUS open_directory(connection_struct *conn, fsp->sent_oplock_break = NO_BREAK_SENT; fsp->is_directory = True; fsp->is_stat = False; + fsp->posix_open = (file_attributes & FILE_FLAG_POSIX_SEMANTICS) ? True : False; + string_set(&fsp->fsp_name,fname); lck = get_share_mode_lock(NULL, fsp->dev, fsp->inode, @@ -2145,7 +2180,11 @@ NTSTATUS create_directory(connection_struct *conn, const char *directory) status = open_directory(conn, directory, &sbuf, FILE_READ_ATTRIBUTES, /* Just a stat open */ FILE_SHARE_NONE, /* Ignored for stat opens */ - FILE_CREATE, 0, NULL, &fsp); + FILE_CREATE, + 0, + FILE_ATTRIBUTE_DIRECTORY, + NULL, + &fsp); if (NT_STATUS_IS_OK(status)) { close_file(fsp, NORMAL_CLOSE); diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c index 7b82a9ad9c..423d6b3a99 100644 --- a/source3/smbd/oplock.c +++ b/source3/smbd/oplock.c @@ -832,6 +832,7 @@ void share_mode_entry_to_message(char *msg, struct share_mode_entry *e) SINO_T_VAL(msg,36,e->inode); SIVAL(msg,44,e->share_file_id); SIVAL(msg,48,e->uid); + SSVAL(msg,52,e->flags); } /**************************************************************************** @@ -852,6 +853,7 @@ void message_to_share_mode_entry(struct share_mode_entry *e, char *msg) e->inode = INO_T_VAL(msg,36); e->share_file_id = (unsigned long)IVAL(msg,44); e->uid = (uint32)IVAL(msg,48); + e->flags = (uint16)SVAL(msg,52); } /**************************************************************************** diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index 655d78cd24..8bccd45f4b 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -3108,9 +3108,9 @@ int reply_exit(connection_struct *conn, int reply_close(connection_struct *conn, char *inbuf,char *outbuf, int size, int dum_buffsize) { + NTSTATUS status = NT_STATUS_OK; int outsize = 0; time_t mtime; - int32 eclass = 0, err = 0; files_struct *fsp = NULL; START_PROFILE(SMBclose); @@ -3138,12 +3138,11 @@ int reply_close(connection_struct *conn, char *inbuf,char *outbuf, int size, * Special case - close NT SMB directory handle. */ DEBUG(3,("close directory fnum=%d\n", fsp->fnum)); - close_file(fsp,NORMAL_CLOSE); + status = close_file(fsp,NORMAL_CLOSE); } else { /* * Close ordinary file. */ - int close_err; DEBUG(3,("close fd=%d fnum=%d (numopen=%d)\n", fsp->fh->fd, fsp->fnum, @@ -3162,17 +3161,12 @@ int reply_close(connection_struct *conn, char *inbuf,char *outbuf, int size, * a disk full error. If not then it was probably an I/O error. */ - if((close_err = close_file(fsp,NORMAL_CLOSE)) != 0) { - errno = close_err; - END_PROFILE(SMBclose); - return (UNIXERROR(ERRHRD,ERRgeneral)); - } + status = close_file(fsp,NORMAL_CLOSE); } - /* We have a cached error */ - if(eclass || err) { + if(!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBclose); - return ERROR_DOS(eclass,err); + return ERROR_NT(status); } END_PROFILE(SMBclose); @@ -3189,7 +3183,7 @@ int reply_writeclose(connection_struct *conn, size_t numtowrite; ssize_t nwritten = -1; int outsize = 0; - int close_err = 0; + NTSTATUS close_status = NT_STATUS_OK; SMB_OFF_T startpos; char *data; time_t mtime; @@ -3223,7 +3217,7 @@ int reply_writeclose(connection_struct *conn, if (numtowrite) { DEBUG(3,("reply_writeclose: zero length write doesn't close file %s\n", fsp->fsp_name )); - close_err = close_file(fsp,NORMAL_CLOSE); + close_status = close_file(fsp,NORMAL_CLOSE); } DEBUG(3,("writeclose fnum=%d num=%d wrote=%d (numopen=%d)\n", @@ -3235,10 +3229,9 @@ int reply_writeclose(connection_struct *conn, return(UNIXERROR(ERRHRD,ERRdiskfull)); } - if(close_err != 0) { - errno = close_err; + if(!NT_STATUS_IS_OK(close_status)) { END_PROFILE(SMBwriteclose); - return(UNIXERROR(ERRHRD,ERRgeneral)); + return ERROR_NT(close_status); } outsize = set_message(outbuf,1,0,True); @@ -3455,7 +3448,7 @@ int reply_printclose(connection_struct *conn, { int outsize = set_message(outbuf,0,0,False); files_struct *fsp = file_fsp(inbuf,smb_vwv0); - int close_err = 0; + NTSTATUS status; START_PROFILE(SMBsplclose); CHECK_FSP(fsp,conn); @@ -3468,12 +3461,11 @@ int reply_printclose(connection_struct *conn, DEBUG(3,("printclose fd=%d fnum=%d\n", fsp->fh->fd,fsp->fnum)); - close_err = close_file(fsp,NORMAL_CLOSE); + status = close_file(fsp,NORMAL_CLOSE); - if(close_err != 0) { - errno = close_err; + if(!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBsplclose); - return(UNIXERROR(ERRHRD,ERRgeneral)); + return ERROR_NT(status); } END_PROFILE(SMBsplclose); @@ -3707,7 +3699,7 @@ static BOOL recursive_rmdir(connection_struct *conn, char *directory) The internals of the rmdir code - called elsewhere. ****************************************************************************/ -BOOL rmdir_internals(connection_struct *conn, const char *directory) +NTSTATUS rmdir_internals(connection_struct *conn, const char *directory) { int ret; SMB_STRUCT_STAT st; @@ -3717,7 +3709,7 @@ BOOL rmdir_internals(connection_struct *conn, const char *directory) notify_fname(conn, NOTIFY_ACTION_REMOVED, FILE_NOTIFY_CHANGE_DIR_NAME, directory); - return True; + return NT_STATUS_OK; } if(((errno == ENOTEMPTY)||(errno == EEXIST)) && lp_veto_files(SNUM(conn))) { @@ -3791,14 +3783,14 @@ BOOL rmdir_internals(connection_struct *conn, const char *directory) if (ret != 0) { DEBUG(3,("rmdir_internals: couldn't remove directory %s : " "%s\n", directory,strerror(errno))); - return False; + return map_nt_error_from_unix(errno); } notify_fname(conn, NOTIFY_ACTION_REMOVED, FILE_NOTIFY_CHANGE_DIR_NAME, directory); - return True; + return NT_STATUS_OK; } /**************************************************************************** @@ -3834,9 +3826,10 @@ int reply_rmdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, } dptr_closepath(directory,SVAL(inbuf,smb_pid)); - if (!rmdir_internals(conn, directory)) { + status = rmdir_internals(conn, directory); + if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBrmdir); - return UNIXERROR(ERRDOS, ERRbadpath); + return ERROR_NT(status); } outsize = set_message(outbuf,0,0,False); @@ -4585,7 +4578,6 @@ NTSTATUS copy_file(char *src, char *dest1,connection_struct *conn, int ofun, uint32 dosattrs; uint32 new_create_disposition; NTSTATUS status; - int close_err; pstrcpy(dest,dest1); if (target_is_directory) { @@ -4670,10 +4662,10 @@ NTSTATUS copy_file(char *src, char *dest1,connection_struct *conn, int ofun, * Thus we don't look at the error return from the * close of fsp1. */ - close_err = close_file(fsp2,NORMAL_CLOSE); + status = close_file(fsp2,NORMAL_CLOSE); - if (close_err != 0) { - return map_nt_error_from_unix(close_err); + if (!NT_STATUS_IS_OK(status)) { + return status; } if (ret != (SMB_OFF_T)src_sbuf.st_size) { diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c index 0394113408..d85ed9877d 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -2529,7 +2529,8 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned SBIG_UINT(pdata,4,((SMB_BIG_UINT)( CIFS_UNIX_POSIX_ACLS_CAP| CIFS_UNIX_POSIX_PATHNAMES_CAP| - CIFS_UNIX_FCNTL_LOCKS_CAP))); + CIFS_UNIX_FCNTL_LOCKS_CAP| + CIFS_UNIX_POSIX_PATH_OPERATIONS_CAP))); break; case SMB_QUERY_POSIX_FS_INFO: @@ -2646,8 +2647,14 @@ cap_low = 0x%x, cap_high = 0x%x\n", mangle_change_to_posix(); } - if (client_unix_cap_low & CIFS_UNIX_FCNTL_LOCKS_CAP) { - lp_set_posix_cifsx_locktype(POSIX_LOCK); + if ((client_unix_cap_low & CIFS_UNIX_FCNTL_LOCKS_CAP) && + !(client_unix_cap_low & CIFS_UNIX_POSIX_PATH_OPERATIONS_CAP)) { + /* Client that knows how to do posix locks, + * but not posix open/mkdir operations. Set a + * default type for read/write checks. */ + + lp_set_posix_default_cifsx_readwrite_locktype(POSIX_LOCK); + } break; } @@ -4845,6 +4852,246 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", tvs); } +/**************************************************************************** + Create a directory with POSIX semantics. +****************************************************************************/ + +static NTSTATUS smb_posix_mkdir(connection_struct *conn, + const char *pdata, + int total_data, + const char *fname) +{ + NTSTATUS status; + SMB_STRUCT_STAT sbuf; + uint32 raw_unixmode; + uint32 mod_unixmode; + mode_t unixmode; + files_struct *fsp; + + if (total_data < 10) { + return NT_STATUS_INVALID_PARAMETER; + } + + ZERO_STRUCT(sbuf); + + raw_unixmode = IVAL(pdata,8); + status = unix_perms_from_wire(conn, &sbuf, raw_unixmode, PERM_NEW_DIR, &unixmode); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + mod_unixmode = (uint32)unixmode | FILE_FLAG_POSIX_SEMANTICS; + + status = open_directory(conn, + fname, + &sbuf, + FILE_READ_ATTRIBUTES, /* Just a stat open */ + FILE_SHARE_NONE, /* Ignored for stat opens */ + FILE_CREATE, + 0, + mod_unixmode, + NULL, + &fsp); + + if (NT_STATUS_IS_OK(status)) { + close_file(fsp, NORMAL_CLOSE); + } + return status; +} + +/**************************************************************************** + Open/Create a file with POSIX semantics. +****************************************************************************/ + +static NTSTATUS smb_posix_open(connection_struct *conn, + char **ppdata, + int total_data, + const char *fname, + SMB_STRUCT_STAT *psbuf, + int *pdata_return_size) +{ + BOOL extended_oplock_granted = False; + const char *pdata = *ppdata; + uint32 flags = 0; + uint32 wire_open_mode = 0; + uint32 raw_unixmode = 0; + uint32 mod_unixmode = 0; + uint32 create_disp = 0; + uint32 access_mask = 0; + uint32 create_options = 0; + NTSTATUS status = NT_STATUS_OK; + mode_t unixmode = (mode_t)0; + files_struct *fsp = NULL; + int oplock_request = 0; + int info = 0; + + if (total_data < 14) { + return NT_STATUS_INVALID_PARAMETER; + } + + flags = IVAL(pdata,0); + oplock_request = (flags & REQUEST_OPLOCK) ? EXCLUSIVE_OPLOCK : 0; + if (oplock_request) { + oplock_request |= (flags & REQUEST_BATCH_OPLOCK) ? BATCH_OPLOCK : 0; + } + + wire_open_mode = IVAL(pdata,4); + + if (wire_open_mode == (SMB_O_CREAT|SMB_O_DIRECTORY)) { + return smb_posix_mkdir(conn, pdata, total_data, fname); + } + + switch (wire_open_mode & SMB_ACCMODE) { + case SMB_O_RDONLY: + access_mask = FILE_READ_DATA; + break; + case SMB_O_WRONLY: + access_mask = FILE_WRITE_DATA; + break; + case SMB_O_RDWR: + access_mask = FILE_READ_DATA|FILE_WRITE_DATA; + break; + default: + DEBUG(5,("smb_posix_open: invalid open mode 0x%x\n", + (unsigned int)wire_open_mode )); + return NT_STATUS_INVALID_PARAMETER; + } + + wire_open_mode &= ~SMB_ACCMODE; + + if((wire_open_mode & (SMB_O_CREAT | SMB_O_EXCL)) == (SMB_O_CREAT | SMB_O_EXCL)) { + create_disp = FILE_CREATE; + } else if((wire_open_mode & (SMB_O_CREAT | SMB_O_TRUNC)) == (SMB_O_CREAT | SMB_O_TRUNC)) { + create_disp = FILE_OVERWRITE_IF; + } else if((wire_open_mode & SMB_O_CREAT) == SMB_O_CREAT) { + create_disp = FILE_OPEN_IF; + } else { + DEBUG(5,("smb_posix_open: invalid create mode 0x%x\n", + (unsigned int)wire_open_mode )); + return NT_STATUS_INVALID_PARAMETER; + } + + raw_unixmode = IVAL(pdata,8); + status = unix_perms_from_wire(conn, + psbuf, + raw_unixmode, + VALID_STAT(*psbuf) ? PERM_EXISTING_FILE : PERM_NEW_FILE, + &unixmode); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + mod_unixmode = (uint32)unixmode | FILE_FLAG_POSIX_SEMANTICS; + + if (wire_open_mode & SMB_O_SYNC) { + create_options |= FILE_WRITE_THROUGH; + } + if (wire_open_mode & SMB_O_APPEND) { + access_mask |= FILE_APPEND_DATA; + } + if (wire_open_mode & SMB_O_DIRECT) { + mod_unixmode |= FILE_FLAG_NO_BUFFERING; + } + + status = open_file_ntcreate(conn, + fname, + psbuf, + access_mask, + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, + create_disp, + 0, /* no create options yet. */ + mod_unixmode, + oplock_request, + &info, + &fsp); + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + if (oplock_request && lp_fake_oplocks(SNUM(conn))) { + extended_oplock_granted = True; + } + + if(oplock_request && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { + extended_oplock_granted = True; + } + + *pdata_return_size = 6; + /* Realloc the data size */ + *ppdata = (char *)SMB_REALLOC(*ppdata,*pdata_return_size); + if (*ppdata == NULL) { + close_file(fsp,ERROR_CLOSE); + return NT_STATUS_NO_MEMORY; + } + + if (extended_oplock_granted) { + if (flags & REQUEST_BATCH_OPLOCK) { + SSVAL(pdata,0, BATCH_OPLOCK_RETURN); + } else { + SSVAL(pdata,0, EXCLUSIVE_OPLOCK_RETURN); + } + } else if (fsp->oplock_type == LEVEL_II_OPLOCK) { + SSVAL(pdata,0, LEVEL_II_OPLOCK_RETURN); + } else { + SSVAL(pdata,0,NO_OPLOCK_RETURN); + } + + SSVAL(pdata,2,fsp->fnum); + SSVAL(pdata,4,SMB_NO_INFO_LEVEL_RETURNED); + return NT_STATUS_OK; +} + +/**************************************************************************** + Delete a file with POSIX semantics. +****************************************************************************/ + +static NTSTATUS smb_posix_unlink(connection_struct *conn, + const char *pdata, + int total_data, + const char *fname, + SMB_STRUCT_STAT *psbuf) +{ + NTSTATUS status = NT_STATUS_OK; + files_struct *fsp = NULL; + int info = 0; + + if (!VALID_STAT(*psbuf)) { + return NT_STATUS_OBJECT_NAME_NOT_FOUND; + } + + if (VALID_STAT_OF_DIR(*psbuf)) { + status = open_directory(conn, + fname, + psbuf, + DELETE_ACCESS, + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, + FILE_OPEN, + FILE_DELETE_ON_CLOSE, + FILE_FLAG_POSIX_SEMANTICS|0777, + &info, + &fsp); + } else { + status = open_file_ntcreate(conn, + fname, + psbuf, + DELETE_ACCESS, + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, + FILE_OPEN, + FILE_DELETE_ON_CLOSE, + FILE_FLAG_POSIX_SEMANTICS|0777, + INTERNAL_OPEN_ONLY, + &info, + &fsp); + } + + if (!NT_STATUS_IS_OK(status)) { + return status; + } + return close_file(fsp, NORMAL_CLOSE); +} + /**************************************************************************** Reply to a TRANS2_SETFILEINFO (set file info by fileid or pathname). ****************************************************************************/ @@ -4861,6 +5108,7 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char pstring fname; files_struct *fsp = NULL; NTSTATUS status = NT_STATUS_OK; + int data_return_size = 0; if (!params) { return ERROR_NT(NT_STATUS_INVALID_PARAMETER); @@ -5148,6 +5396,9 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char case SMB_SET_POSIX_LOCK: { + if (tran_call == TRANSACT2_SETFILEINFO) { + return ERROR_NT(NT_STATUS_INVALID_LEVEL); + } status = smb_set_posix_lock(conn, inbuf, length, @@ -5157,6 +5408,37 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char break; } + case SMB_POSIX_PATH_OPEN: + { + if (tran_call != TRANSACT2_SETPATHINFO) { + /* We must have a pathname for this. */ + return ERROR_NT(NT_STATUS_INVALID_LEVEL); + } + + status = smb_posix_open(conn, + ppdata, + total_data, + fname, + &sbuf, + &data_return_size); + break; + } + + case SMB_POSIX_PATH_UNLINK: + { + if (tran_call != TRANSACT2_SETPATHINFO) { + /* We must have a pathname for this. */ + return ERROR_NT(NT_STATUS_INVALID_LEVEL); + } + + status = smb_posix_unlink(conn, + pdata, + total_data, + fname, + &sbuf); + break; + } + default: return ERROR_NT(NT_STATUS_INVALID_LEVEL); } @@ -5178,7 +5460,7 @@ static int call_trans2setfilepathinfo(connection_struct *conn, char *inbuf, char } SSVAL(params,0,0); - send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0, max_data_bytes); + send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, data_return_size, max_data_bytes); return -1; } -- cgit