diff options
author | Jeremy Allison <jra@samba.org> | 2006-12-30 03:03:33 +0000 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 2006-12-30 03:03:33 +0000 |
commit | 5b8b7e86d207fac85bba2cc980532ff336dedc01 (patch) | |
tree | 680666af17eb08d0ba4a2a9afc7b28d8da703b8f | |
parent | 23d86c7a8e004f5908dc70eccfb8e6e342b760ab (diff) | |
download | samba-5b8b7e86d207fac85bba2cc980532ff336dedc01.tar.gz samba-5b8b7e86d207fac85bba2cc980532ff336dedc01.tar.xz samba-5b8b7e86d207fac85bba2cc980532ff336dedc01.zip |
r20413: Merge Volker's open directory changes to 3.0.24.
I think they're correct, modulo more testing of
error codes.
Jeremy.
-rw-r--r-- | source/lib/util.c | 31 | ||||
-rw-r--r-- | source/printing/nt_printing.c | 2 | ||||
-rw-r--r-- | source/smbd/dosmode.c | 27 | ||||
-rw-r--r-- | source/smbd/nttrans.c | 86 | ||||
-rw-r--r-- | source/smbd/open.c | 390 | ||||
-rw-r--r-- | source/smbd/posix_acls.c | 19 | ||||
-rw-r--r-- | source/smbd/reply.c | 67 | ||||
-rw-r--r-- | source/smbd/trans2.c | 37 | ||||
-rw-r--r-- | source/smbd/vfs.c | 25 |
9 files changed, 387 insertions, 297 deletions
diff --git a/source/lib/util.c b/source/lib/util.c index 9ac0b376123..5e2588e5b90 100644 --- a/source/lib/util.c +++ b/source/lib/util.c @@ -2620,6 +2620,37 @@ char *parent_dirname(const char *path) return dirpath; } +BOOL parent_dirname_talloc(TALLOC_CTX *mem_ctx, const char *dir, + char **parent, const char **name) +{ + char *p; + ptrdiff_t len; + + p = strrchr_m(dir, '/'); /* Find final '/', if any */ + + if (p == NULL) { + if (!(*parent = talloc_strdup(mem_ctx, "."))) { + return False; + } + if (name) { + *name = ""; + } + return True; + } + + len = p-dir; + + if (!(*parent = TALLOC_ARRAY(mem_ctx, char, len+1))) { + return False; + } + memcpy(*parent, dir, len); + (*parent)[len] = '\0'; + + if (name) { + *name = p+1; + } + return True; +} /******************************************************************* Determine if a pattern contains any Microsoft wildcard characters. diff --git a/source/printing/nt_printing.c b/source/printing/nt_printing.c index b74763b2e5e..541a60fef14 100644 --- a/source/printing/nt_printing.c +++ b/source/printing/nt_printing.c @@ -1800,7 +1800,7 @@ WERROR move_driver_to_download_area(NT_PRINTER_DRIVER_INFO_LEVEL driver_abstract DEBUG(5,("Creating first directory\n")); slprintf(new_dir, sizeof(new_dir)-1, "%s/%d", architecture, driver->cversion); driver_unix_convert(new_dir, conn, NULL, &bad_path, &st); - mkdir_internal(conn, new_dir, bad_path); + create_directory(conn, new_dir); /* For each driver file, archi\filexxx.yyy, if there is a duplicate file * listed for this driver which has already been moved, skip it (note: diff --git a/source/smbd/dosmode.c b/source/smbd/dosmode.c index 260a8dadbd6..1172fe3e6b5 100644 --- a/source/smbd/dosmode.c +++ b/source/smbd/dosmode.c @@ -59,7 +59,7 @@ static uint32 set_offline_flag(connection_struct *conn, const char *const path) /**************************************************************************** Change a dos mode to a unix mode. Base permission for files: - if creating file and inheriting + if creating file and inheriting (i.e. parent_dir != NULL) apply read/write bits from parent directory. else everybody gets read bit set @@ -79,23 +79,26 @@ static uint32 set_offline_flag(connection_struct *conn, const char *const path) } ****************************************************************************/ -mode_t unix_mode(connection_struct *conn, int dosmode, const char *fname, BOOL creating_file) +mode_t unix_mode(connection_struct *conn, int dosmode, const char *fname, + const char *inherit_from_dir) { mode_t result = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH); - mode_t dir_mode = 0; /* Mode of the parent directory if inheriting. */ + mode_t dir_mode = 0; /* Mode of the inherit_from directory if + * inheriting. */ if (!lp_store_dos_attributes(SNUM(conn)) && IS_DOS_READONLY(dosmode)) { result &= ~(S_IWUSR | S_IWGRP | S_IWOTH); } - if (fname && creating_file && lp_inherit_perms(SNUM(conn))) { - char *dname; + if (fname && (inherit_from_dir != NULL) + && lp_inherit_perms(SNUM(conn))) { SMB_STRUCT_STAT sbuf; - dname = parent_dirname(fname); - DEBUG(2,("unix_mode(%s) inheriting from %s\n",fname,dname)); - if (SMB_VFS_STAT(conn,dname,&sbuf) != 0) { - DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n",fname,dname,strerror(errno))); + DEBUG(2, ("unix_mode(%s) inheriting from %s\n", fname, + inherit_from_dir)); + if (SMB_VFS_STAT(conn, inherit_from_dir, &sbuf) != 0) { + DEBUG(4,("unix_mode(%s) failed, [dir %s]: %s\n", fname, + inherit_from_dir, strerror(errno))); return(0); /* *** shouldn't happen! *** */ } @@ -429,7 +432,9 @@ uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf) chmod a file - but preserve some bits. ********************************************************************/ -int file_set_dosmode(connection_struct *conn, const char *fname, uint32 dosmode, SMB_STRUCT_STAT *st, BOOL creating_file) +int file_set_dosmode(connection_struct *conn, const char *fname, + uint32 dosmode, SMB_STRUCT_STAT *st, + const char *parent_dir) { SMB_STRUCT_STAT st1; int mask=0; @@ -462,7 +467,7 @@ int file_set_dosmode(connection_struct *conn, const char *fname, uint32 dosmode, return 0; } - unixmode = unix_mode(conn,dosmode,fname, creating_file); + unixmode = unix_mode(conn,dosmode,fname, parent_dir); /* preserve the s bits */ mask |= (S_ISUID | S_ISGID); diff --git a/source/smbd/nttrans.c b/source/smbd/nttrans.c index a0dc89a31a5..b429451ea88 100644 --- a/source/smbd/nttrans.c +++ b/source/smbd/nttrans.c @@ -68,8 +68,8 @@ static char *nttrans_realloc(char **ptr, size_t size) HACK ! Always assumes smb_setup field is zero. ****************************************************************************/ -static int send_nt_replies(char *inbuf, char *outbuf, int bufsize, NTSTATUS nt_error, char *params, - int paramsize, char *pdata, int datasize) +int send_nt_replies(char *outbuf, int bufsize, NTSTATUS nt_error, + char *params, int paramsize, char *pdata, int datasize) { int data_to_send = datasize; int params_to_send = paramsize; @@ -477,9 +477,10 @@ int reply_ntcreate_and_X(connection_struct *conn, START_PROFILE(SMBntcreateX); - DEBUG(10,("reply_ntcreate_and_X: flags = 0x%x, access_mask = 0x%x \ -file_attributes = 0x%x, share_access = 0x%x, create_disposition = 0x%x \ -create_options = 0x%x root_dir_fid = 0x%x\n", + DEBUG(10,("reply_ntcreate_and_X: flags = 0x%x, access_mask = 0x%x " + "file_attributes = 0x%x, share_access = 0x%x, " + "create_disposition = 0x%x create_options = 0x%x " + "root_dir_fid = 0x%x\n", (unsigned int)flags, (unsigned int)access_mask, (unsigned int)file_attributes, @@ -686,6 +687,10 @@ create_options = 0x%x root_dir_fid = 0x%x\n", restore_case_semantics(conn, file_attributes); if(!NT_STATUS_IS_OK(status)) { + if (!use_nt_status() && NT_STATUS_EQUAL( + status, NT_STATUS_OBJECT_NAME_COLLISION)) { + status = NT_STATUS_DOS(ERRDOS, ERRfilexists); + } END_PROFILE(SMBntcreateX); return ERROR_NT(status); } @@ -758,6 +763,10 @@ create_options = 0x%x root_dir_fid = 0x%x\n", if(!NT_STATUS_IS_OK(status)) { restore_case_semantics(conn, file_attributes); + if (!use_nt_status() && NT_STATUS_EQUAL( + status, NT_STATUS_OBJECT_NAME_COLLISION)) { + status = NT_STATUS_DOS(ERRDOS, ERRfilexists); + } END_PROFILE(SMBntcreateX); return ERROR_NT(status); } @@ -959,7 +968,7 @@ static int do_nt_transact_create_pipe( connection_struct *conn, char *inbuf, cha DEBUG(5,("do_nt_transact_create_pipe: open name = %s\n", fname)); /* Send the required number of replies */ - send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, params, 69, *ppdata, 0); + send_nt_replies(outbuf, bufsize, NT_STATUS_OK, params, 69, *ppdata, 0); return -1; } @@ -1283,19 +1292,12 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o } if (ea_len) { - ctx = talloc_init("NTTRANS_CREATE_EA"); - if (!ctx) { - talloc_destroy(ctx); - restore_case_semantics(conn, file_attributes); - return ERROR_NT(NT_STATUS_NO_MEMORY); - } - pdata = data + sd_len; /* We have already checked that ea_len <= data_count here. */ - ea_list = read_nttrans_ea_list(ctx, pdata, ea_len); + ea_list = read_nttrans_ea_list(tmp_talloc_ctx(), pdata, + ea_len); if (!ea_list ) { - talloc_destroy(ctx); restore_case_semantics(conn, file_attributes); return ERROR_NT(NT_STATUS_INVALID_PARAMETER); } @@ -1309,7 +1311,6 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o /* Can't open a temp directory. IFS kit test. */ if (file_attributes & FILE_ATTRIBUTE_TEMPORARY) { - talloc_destroy(ctx); restore_case_semantics(conn, file_attributes); return ERROR_NT(NT_STATUS_INVALID_PARAMETER); } @@ -1329,7 +1330,6 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o create_options, &info, &fsp); if(!NT_STATUS_IS_OK(status)) { - talloc_destroy(ctx); restore_case_semantics(conn, file_attributes); return ERROR_NT(status); } @@ -1370,12 +1370,10 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o create_options, &info, &fsp); if(!NT_STATUS_IS_OK(status)) { - talloc_destroy(ctx); restore_case_semantics(conn, file_attributes); return ERROR_NT(status); } } else { - talloc_destroy(ctx); restore_case_semantics(conn, file_attributes); if (open_was_deferred(SVAL(inbuf,smb_mid))) { /* We have re-scheduled this call. */ @@ -1416,7 +1414,6 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o if (ea_len && (info == FILE_WAS_CREATED)) { status = set_ea(conn, fsp, fname, ea_list); - talloc_destroy(ctx); if (!NT_STATUS_IS_OK(status)) { close_file(fsp,ERROR_CLOSE); restore_case_semantics(conn, file_attributes); @@ -1480,8 +1477,12 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o p = params; if (extended_oplock_granted) { - SCVAL(p,0, BATCH_OPLOCK_RETURN); - } else if (LEVEL_II_OPLOCK_TYPE(fsp->oplock_type)) { + if (flags & REQUEST_BATCH_OPLOCK) { + SCVAL(p,0, BATCH_OPLOCK_RETURN); + } else { + SCVAL(p,0, EXCLUSIVE_OPLOCK_RETURN); + } + } else if (fsp->oplock_type == LEVEL_II_OPLOCK) { SCVAL(p,0, LEVEL_II_OPLOCK_RETURN); } else { SCVAL(p,0,NO_OPLOCK_RETURN); @@ -1531,7 +1532,7 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o DEBUG(5,("call_nt_transact_create: open name = %s\n", fname)); /* Send the required number of replies */ - send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, params, 69, *ppdata, 0); + send_nt_replies(outbuf, bufsize, NT_STATUS_OK, params, 69, *ppdata, 0); return -1; } @@ -1695,7 +1696,8 @@ static NTSTATUS copy_internals(connection_struct *conn, char *oldname, char *new /* 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 case. JRA */ - file_set_dosmode(conn, newname, fattr, &sbuf2, True); + file_set_dosmode(conn, newname, fattr, &sbuf2, + parent_dirname(newname)); if (ret < (SMB_OFF_T)sbuf1.st_size) { return NT_STATUS_DISK_FULL; @@ -1883,7 +1885,7 @@ static int call_nt_transact_rename(connection_struct *conn, char *inbuf, char *o /* * Rename was successful. */ - send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, NULL, 0, NULL, 0); + send_nt_replies(outbuf, bufsize, NT_STATUS_OK, NULL, 0, NULL, 0); DEBUG(3,("nt transact rename from = %s, to = %s succeeded.\n", fsp->fsp_name, new_name)); @@ -1978,8 +1980,8 @@ static int call_nt_transact_query_security_desc(connection_struct *conn, char *i if(max_data_count < sd_size) { - send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_BUFFER_TOO_SMALL, - params, 4, *ppdata, 0); + send_nt_replies(outbuf, bufsize, NT_STATUS_BUFFER_TOO_SMALL, + params, 4, *ppdata, 0); talloc_destroy(mem_ctx); return -1; } @@ -2027,7 +2029,8 @@ security descriptor.\n")); talloc_destroy(mem_ctx); - send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, params, 4, data, (int)sd_size); + send_nt_replies(outbuf, bufsize, NT_STATUS_OK, params, 4, data, + (int)sd_size); return -1; } @@ -2073,7 +2076,7 @@ static int call_nt_transact_set_security_desc(connection_struct *conn, char *inb done: - send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, NULL, 0, NULL, 0); + send_nt_replies(outbuf, bufsize, NT_STATUS_OK, NULL, 0, NULL, 0); return -1; } @@ -2119,7 +2122,8 @@ static int call_nt_transact_ioctl(connection_struct *conn, char *inbuf, char *ou so we can know if we need to pre-allocate or not */ DEBUG(10,("FSCTL_SET_SPARSE: called on FID[0x%04X](but not implemented)\n", fidnum)); - send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, NULL, 0, NULL, 0); + send_nt_replies(outbuf, bufsize, NT_STATUS_OK, NULL, 0, NULL, + 0); return -1; case FSCTL_0x000900C0: @@ -2128,7 +2132,8 @@ static int call_nt_transact_ioctl(connection_struct *conn, char *inbuf, char *ou */ DEBUG(10,("FSCTL_0x000900C0: called on FID[0x%04X](but not implemented)\n",fidnum)); - send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, NULL, 0, NULL, 0); + send_nt_replies(outbuf, bufsize, NT_STATUS_OK, NULL, 0, NULL, + 0); return -1; case FSCTL_GET_REPARSE_POINT: @@ -2137,7 +2142,8 @@ static int call_nt_transact_ioctl(connection_struct *conn, char *inbuf, char *ou */ DEBUG(10,("FSCTL_GET_REPARSE_POINT: called on FID[0x%04X](but not implemented)\n",fidnum)); - send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_NOT_A_REPARSE_POINT, NULL, 0, NULL, 0); + send_nt_replies(outbuf, bufsize, NT_STATUS_NOT_A_REPARSE_POINT, + NULL, 0, NULL, 0); return -1; case FSCTL_SET_REPARSE_POINT: @@ -2146,7 +2152,8 @@ static int call_nt_transact_ioctl(connection_struct *conn, char *inbuf, char *ou */ DEBUG(10,("FSCTL_SET_REPARSE_POINT: called on FID[0x%04X](but not implemented)\n",fidnum)); - send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_NOT_A_REPARSE_POINT, NULL, 0, NULL, 0); + send_nt_replies(outbuf, bufsize, NT_STATUS_NOT_A_REPARSE_POINT, + NULL, 0, NULL, 0); return -1; case FSCTL_GET_SHADOW_COPY_DATA: /* don't know if this name is right...*/ @@ -2259,7 +2266,8 @@ static int call_nt_transact_ioctl(connection_struct *conn, char *inbuf, char *ou talloc_destroy(shadow_data->mem_ctx); - send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, NULL, 0, pdata, data_count); + send_nt_replies(outbuf, bufsize, NT_STATUS_OK, NULL, 0, + pdata, data_count); return -1; } @@ -2311,7 +2319,8 @@ static int call_nt_transact_ioctl(connection_struct *conn, char *inbuf, char *ou */ /* this works for now... */ - send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, NULL, 0, NULL, 0); + send_nt_replies(outbuf, bufsize, NT_STATUS_OK, NULL, 0, + NULL, 0); return -1; } default: @@ -2577,7 +2586,8 @@ static int call_nt_transact_get_user_quota(connection_struct *conn, char *inbuf, break; } - send_nt_replies(inbuf, outbuf, bufsize, nt_status, params, param_len, pdata, data_len); + send_nt_replies(outbuf, bufsize, nt_status, params, param_len, + pdata, data_len); return -1; } @@ -2694,7 +2704,8 @@ static int call_nt_transact_set_user_quota(connection_struct *conn, char *inbuf, return ERROR_DOS(ERRSRV,ERRerror); } - send_nt_replies(inbuf, outbuf, bufsize, NT_STATUS_OK, params, param_len, pdata, data_len); + send_nt_replies(outbuf, bufsize, NT_STATUS_OK, params, param_len, + pdata, data_len); return -1; } @@ -2865,6 +2876,7 @@ int reply_nttrans(connection_struct *conn, state->total_param = IVAL(inbuf, smb_nt_TotalParameterCount); state->param = NULL; state->max_data_return = IVAL(inbuf,smb_nt_MaxDataCount); + state->max_param_return = IVAL(inbuf,smb_nt_MaxParameterCount); /* setup count is in *words* */ state->setup_count = 2*CVAL(inbuf,smb_nt_SetupCount); diff --git a/source/smbd/open.c b/source/smbd/open.c index 9d9b7a074c1..8d67afd6998 100644 --- a/source/smbd/open.c +++ b/source/smbd/open.c @@ -84,100 +84,111 @@ int fd_close(struct connection_struct *conn, Do this by fd if possible. ****************************************************************************/ -static void change_owner_to_parent(connection_struct *conn, - files_struct *fsp, - const char *fname, - SMB_STRUCT_STAT *psbuf) +static void change_file_owner_to_parent(connection_struct *conn, + const char *inherit_from_dir, + files_struct *fsp) { - const char *parent_path = parent_dirname(fname); SMB_STRUCT_STAT parent_st; int ret; - ret = SMB_VFS_STAT(conn, parent_path, &parent_st); + ret = SMB_VFS_STAT(conn, inherit_from_dir, &parent_st); if (ret == -1) { - DEBUG(0,("change_owner_to_parent: failed to stat parent " + DEBUG(0,("change_file_owner_to_parent: failed to stat parent " "directory %s. Error was %s\n", - parent_path, strerror(errno) )); + inherit_from_dir, strerror(errno) )); return; } - if (fsp && fsp->fh->fd != -1) { - become_root(); - ret = SMB_VFS_FCHOWN(fsp, fsp->fh->fd, parent_st.st_uid, (gid_t)-1); - unbecome_root(); - if (ret == -1) { - DEBUG(0,("change_owner_to_parent: failed to fchown " - "file %s to parent directory uid %u. Error " - "was %s\n", fname, - (unsigned int)parent_st.st_uid, - strerror(errno) )); - } + become_root(); + ret = SMB_VFS_FCHOWN(fsp, fsp->fh->fd, parent_st.st_uid, (gid_t)-1); + unbecome_root(); + if (ret == -1) { + DEBUG(0,("change_file_owner_to_parent: failed to fchown " + "file %s to parent directory uid %u. Error " + "was %s\n", fsp->fsp_name, + (unsigned int)parent_st.st_uid, + strerror(errno) )); + } - DEBUG(10,("change_owner_to_parent: changed new file %s to " - "parent directory uid %u.\n", fname, - (unsigned int)parent_st.st_uid )); + DEBUG(10,("change_file_owner_to_parent: changed new file %s to " + "parent directory uid %u.\n", fsp->fsp_name, + (unsigned int)parent_st.st_uid )); +} - } else { - /* We've already done an lstat into psbuf, and we know it's a - directory. If we can cd into the directory and the dev/ino - are the same then we can safely chown without races as - we're locking the directory in place by being in it. This - should work on any UNIX (thanks tridge :-). JRA. - */ - - pstring saved_dir; - SMB_STRUCT_STAT sbuf; - - if (!vfs_GetWd(conn,saved_dir)) { - DEBUG(0,("change_owner_to_parent: failed to get " - "current working directory\n")); - return; - } +static void change_dir_owner_to_parent(connection_struct *conn, + const char *inherit_from_dir, + const char *fname, + SMB_STRUCT_STAT *psbuf) +{ + pstring saved_dir; + SMB_STRUCT_STAT sbuf; + SMB_STRUCT_STAT parent_st; + int ret; - /* Chdir into the new path. */ - if (vfs_ChDir(conn, fname) == -1) { - DEBUG(0,("change_owner_to_parent: failed to change " - "current working directory to %s. Error " - "was %s\n", fname, strerror(errno) )); - goto out; - } + ret = SMB_VFS_STAT(conn, inherit_from_dir, &parent_st); + if (ret == -1) { + DEBUG(0,("change_dir_owner_to_parent: failed to stat parent " + "directory %s. Error was %s\n", + inherit_from_dir, strerror(errno) )); + return; + } - if (SMB_VFS_STAT(conn,".",&sbuf) == -1) { - DEBUG(0,("change_owner_to_parent: failed to stat " - "directory '.' (%s) Error was %s\n", - fname, strerror(errno))); - goto out; - } + /* We've already done an lstat into psbuf, and we know it's a + directory. If we can cd into the directory and the dev/ino + are the same then we can safely chown without races as + we're locking the directory in place by being in it. This + should work on any UNIX (thanks tridge :-). JRA. + */ - /* Ensure we're pointing at the same place. */ - if (sbuf.st_dev != psbuf->st_dev || - sbuf.st_ino != psbuf->st_ino || - sbuf.st_mode != psbuf->st_mode ) { - DEBUG(0,("change_owner_to_parent: " - "device/inode/mode on directory %s changed. " - "Refusing to chown !\n", fname )); - goto out; - } + if (!vfs_GetWd(conn,saved_dir)) { + DEBUG(0,("change_dir_owner_to_parent: failed to get " + "current working directory\n")); + return; + } - become_root(); - ret = SMB_VFS_CHOWN(conn, ".", parent_st.st_uid, (gid_t)-1); - unbecome_root(); - if (ret == -1) { - DEBUG(10,("change_owner_to_parent: failed to chown " - "directory %s to parent directory uid %u. " - "Error was %s\n", fname, - (unsigned int)parent_st.st_uid, strerror(errno) )); - goto out; - } + /* Chdir into the new path. */ + if (vfs_ChDir(conn, fname) == -1) { + DEBUG(0,("change_dir_owner_to_parent: failed to change " + "current working directory to %s. Error " + "was %s\n", fname, strerror(errno) )); + goto out; + } - DEBUG(10,("change_owner_to_parent: changed ownership of new " - "directory %s to parent directory uid %u.\n", - fname, (unsigned int)parent_st.st_uid )); + if (SMB_VFS_STAT(conn,".",&sbuf) == -1) { + DEBUG(0,("change_dir_owner_to_parent: failed to stat " + "directory '.' (%s) Error was %s\n", + fname, strerror(errno))); + goto out; + } - out: + /* Ensure we're pointing at the same place. */ + if (sbuf.st_dev != psbuf->st_dev || + sbuf.st_ino != psbuf->st_ino || + sbuf.st_mode != psbuf->st_mode ) { + DEBUG(0,("change_dir_owner_to_parent: " + "device/inode/mode on directory %s changed. " + "Refusing to chown !\n", fname )); + goto out; + } - vfs_ChDir(conn,saved_dir); + become_root(); + ret = SMB_VFS_CHOWN(conn, ".", parent_st.st_uid, (gid_t)-1); + unbecome_root(); + if (ret == -1) { + DEBUG(10,("change_dir_owner_to_parent: failed to chown " + "directory %s to parent directory uid %u. " + "Error was %s\n", fname, + (unsigned int)parent_st.st_uid, strerror(errno) )); + goto out; } + + DEBUG(10,("change_dir_owner_to_parent: changed ownership of new " + "directory %s to parent directory uid %u.\n", + fname, (unsigned int)parent_st.st_uid )); + + out: + + vfs_ChDir(conn,saved_dir); } /**************************************************************************** @@ -186,6 +197,7 @@ static void change_owner_to_parent(connection_struct *conn, static NTSTATUS open_file(files_struct *fsp, connection_struct *conn, + const char *parent_dir, const char *fname, SMB_STRUCT_STAT *psbuf, int flags, @@ -282,9 +294,19 @@ static NTSTATUS open_file(files_struct *fsp, return map_nt_error_from_unix(errno); } - /* Inherit the ACL if the file was created. */ if ((local_flags & O_CREAT) && !file_existed) { - inherit_access_acl(conn, fname, unx_mode); + + /* Inherit the ACL if required */ + if (lp_inherit_perms(SNUM(conn))) { + inherit_access_acl(conn, parent_dir, fname, + unx_mode); + } + + /* Change the owner if required. */ + if (lp_inherit_owner(SNUM(conn))) { + change_file_owner_to_parent(conn, parent_dir, + fsp); + } } } else { @@ -1105,6 +1127,8 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, uint32 open_access_mask = access_mask; NTSTATUS status; int ret_flock; + char *parent_dir; + const char *newname; if (conn->printer) { /* @@ -1121,9 +1145,15 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, return print_fsp_open(conn, fname, result); } + if (!parent_dirname_talloc(tmp_talloc_ctx(), fname, &parent_dir, + &newname)) { + 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, True); + 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 " @@ -1175,7 +1205,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, /* this is for OS/2 long file names - say we don't support them */ if (!lp_posix_pathnames() && strstr(fname,".+,;=[].")) { /* OS/2 Workplace shell fix may be main code stream in a later - * release. */ + * release. */ DEBUG(5,("open_file_ntcreate: OS/2 long filenames are not " "supported.\n")); if (use_nt_status()) { @@ -1535,8 +1565,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, */ if ((flags2 & O_CREAT) && lp_inherit_acls(SNUM(conn)) && - (def_acl = directory_has_default_acl(conn, - parent_dirname(fname)))) { + (def_acl = directory_has_default_acl(conn, parent_dir))) { unx_mode = 0777; } @@ -1550,8 +1579,8 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, * open_file strips any O_TRUNC flags itself. */ - fsp_open = open_file(fsp,conn,fname,psbuf,flags|flags2,unx_mode, - access_mask, open_access_mask); + fsp_open = open_file(fsp, conn, parent_dir, fname, psbuf, flags|flags2, + unx_mode, access_mask, open_access_mask); if (!NT_STATUS_IS_OK(fsp_open)) { if (lck != NULL) { @@ -1639,7 +1668,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, ret_flock = SMB_VFS_KERNEL_FLOCK(fsp, fsp->fh->fd, share_access); if(ret_flock == -1 ){ - talloc_free(lck); + TALLOC_FREE(lck); fd_close(conn, fsp); file_free(fsp); @@ -1689,11 +1718,6 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, } } else { info = FILE_WAS_CREATED; - /* Change the owner if required. */ - if (lp_inherit_owner(SNUM(conn))) { - change_owner_to_parent(conn, fsp, fsp->fsp_name, - psbuf); - } } if (pinfo) { @@ -1741,7 +1765,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, lp_store_dos_attributes(SNUM(conn))) { file_set_dosmode(conn, fname, new_dos_attributes | aARCH, NULL, - True); + parent_dir); } } @@ -1821,7 +1845,8 @@ NTSTATUS open_file_fchmod(connection_struct *conn, const char *fname, /* note! we must use a non-zero desired access or we don't get a real file descriptor. Oh what a twisted web we weave. */ - status = open_file(fsp,conn,fname,psbuf,O_WRONLY,0,FILE_WRITE_DATA,FILE_WRITE_DATA); + status = open_file(fsp, conn, NULL, fname, psbuf, O_WRONLY, 0, + FILE_WRITE_DATA, FILE_WRITE_DATA); /* * This is not a user visible file open. @@ -1849,6 +1874,73 @@ int close_file_fchmod(files_struct *fsp) return ret; } +static NTSTATUS mkdir_internal(connection_struct *conn, const char *name, + SMB_STRUCT_STAT *psbuf) +{ + int ret= -1; + mode_t mode; + char *parent_dir; + const char *dirname; + + if(!CAN_WRITE(conn)) { + DEBUG(5,("mkdir_internal: failing create on read-only share " + "%s\n", lp_servicename(SNUM(conn)))); + return NT_STATUS_ACCESS_DENIED; + } + + if (!check_name(name, conn)) { + return map_nt_error_from_unix(errno); + } + + if (!parent_dirname_talloc(tmp_talloc_ctx(), name, &parent_dir, + &dirname)) { + return NT_STATUS_NO_MEMORY; + } + + mode = unix_mode(conn, aDIR, name, parent_dir); + + if ((ret=SMB_VFS_MKDIR(conn, name, mode)) != 0) { + return map_nt_error_from_unix(errno); + } + + /* Ensure we're checking for a symlink here.... */ + /* We don't want to get caught by a symlink racer. */ + + if (SMB_VFS_LSTAT(conn, name, psbuf) == -1) { + DEBUG(2, ("Could not stat directory '%s' just created: %s\n", + name, strerror(errno))); + return map_nt_error_from_unix(errno); + } + + if (!S_ISDIR(psbuf->st_mode)) { + DEBUG(0, ("Directory just '%s' created is not a directory\n", + name)); + return NT_STATUS_ACCESS_DENIED; + } + + if (lp_inherit_perms(SNUM(conn))) { + 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)); + } + + /* Change the owner if required. */ + if (lp_inherit_owner(SNUM(conn))) { + change_dir_owner_to_parent(conn, parent_dir, name, psbuf); + } + + return NT_STATUS_OK; +} + /**************************************************************************** Open a directory from an NT SMB call. ****************************************************************************/ @@ -1865,7 +1957,6 @@ NTSTATUS open_directory(connection_struct *conn, { files_struct *fsp = NULL; BOOL dir_existed = VALID_STAT(*psbuf) ? True : False; - BOOL create_dir = False; struct share_mode_lock *lck = NULL; NTSTATUS status; int info = 0; @@ -1886,44 +1977,53 @@ NTSTATUS open_directory(connection_struct *conn, switch( create_disposition ) { case FILE_OPEN: - /* If directory exists open. If directory doesn't - * exist error. */ - if (!dir_existed) { - DEBUG(5,("open_directory: FILE_OPEN requested " - "for directory %s and it doesn't " - "exist.\n", fname )); - return NT_STATUS_OBJECT_NAME_NOT_FOUND; - } + info = FILE_WAS_OPENED; + + /* + * We want to follow symlinks here. + */ + + if (SMB_VFS_STAT(conn, fname, psbuf) != 0) { + return map_nt_error_from_unix(errno); + } + break; case FILE_CREATE: + /* If directory exists error. If directory doesn't * exist create. */ - if (dir_existed) { - DEBUG(5,("open_directory: FILE_CREATE " - "requested for directory %s and it " - "already exists.\n", fname )); - if (use_nt_status()) { - return NT_STATUS_OBJECT_NAME_COLLISION; - } else { - return NT_STATUS_DOS(ERRDOS, - ERRfilexists); - } + + status = mkdir_internal(conn, fname, psbuf); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(2, ("open_directory: unable to create " + "%s. Error was %s\n", fname, + nt_errstr(status))); + return status; } - create_dir = True; + info = FILE_WAS_CREATED; break; case FILE_OPEN_IF: - /* If directory exists open. If directory doesn't - * exist create. */ - if (!dir_existed) { - create_dir = True; + /* + * If directory exists open. If directory doesn't + * exist create. + */ + + status = mkdir_internal(conn, fname, psbuf); + + if (NT_STATUS_IS_OK(status)) { info = FILE_WAS_CREATED; - } else { + } + + if (NT_STATUS_EQUAL(status, + NT_STATUS_OBJECT_NAME_COLLISION)) { info = FILE_WAS_OPENED; + status = NT_STATUS_OK; } + break; case FILE_SUPERSEDE: @@ -1936,35 +2036,10 @@ NTSTATUS open_directory(connection_struct *conn, return NT_STATUS_INVALID_PARAMETER; } - if (create_dir) { - /* - * Try and create the directory. - */ - - /* We know bad_path is false as it's caught earlier. */ - - status = mkdir_internal(conn, fname, False); - - if (!NT_STATUS_IS_OK(status)) { - DEBUG(2,("open_directory: unable to create %s. " - "Error was %s\n", fname, strerror(errno) )); - /* Ensure we return the correct NT status to the - * client. */ - return status; - } - - /* Ensure we're checking for a symlink here.... */ - /* We don't want to get caught by a symlink racer. */ - - if(SMB_VFS_LSTAT(conn,fname, psbuf) != 0) { - return map_nt_error_from_unix(errno); - } - - if(!S_ISDIR(psbuf->st_mode)) { - DEBUG(0,("open_directory: %s is not a directory !\n", - fname )); - return NT_STATUS_NOT_A_DIRECTORY; - } + if(!S_ISDIR(psbuf->st_mode)) { + DEBUG(5,("open_directory: %s is not a directory !\n", + fname )); + return NT_STATUS_NOT_A_DIRECTORY; } status = file_new(conn, &fsp); @@ -2036,11 +2111,6 @@ NTSTATUS open_directory(connection_struct *conn, TALLOC_FREE(lck); - /* Change the owner if required. */ - if ((info == FILE_WAS_CREATED) && lp_inherit_owner(SNUM(conn))) { - change_owner_to_parent(conn, fsp, fsp->fsp_name, psbuf); - } - if (pinfo) { *pinfo = info; } @@ -2051,6 +2121,26 @@ NTSTATUS open_directory(connection_struct *conn, return NT_STATUS_OK; } +NTSTATUS create_directory(connection_struct *conn, const char *directory) +{ + NTSTATUS status; + SMB_STRUCT_STAT sbuf; + files_struct *fsp; + + SET_STAT_INVALID(sbuf); + + 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); + + if (NT_STATUS_IS_OK(status)) { + close_file(fsp, NORMAL_CLOSE); + } + + return status; +} + /**************************************************************************** Open a pseudo-file (no locking checks - a 'stat' open). ****************************************************************************/ diff --git a/source/smbd/posix_acls.c b/source/smbd/posix_acls.c index 4cdb0908ce8..ceeff847214 100644 --- a/source/smbd/posix_acls.c +++ b/source/smbd/posix_acls.c @@ -1878,7 +1878,10 @@ static mode_t create_default_mode(files_struct *fsp, BOOL interitable_mode) int snum = SNUM(fsp->conn); mode_t and_bits = (mode_t)0; mode_t or_bits = (mode_t)0; - mode_t mode = interitable_mode ? unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name, False) : S_IRUSR; + mode_t mode = interitable_mode + ? unix_mode( fsp->conn, FILE_ATTRIBUTE_ARCHIVE, fsp->fsp_name, + NULL ) + : S_IRUSR; if (fsp->is_directory) mode |= (S_IWUSR|S_IXUSR); @@ -3457,19 +3460,17 @@ int chmod_acl(connection_struct *conn, const char *name, mode_t mode) } /**************************************************************************** - If "inherit permissions" is set and the parent directory has no default - ACL but it does have an Access ACL, inherit this Access ACL to file name. + If the parent directory has no default ACL but it does have an Access ACL, + inherit this Access ACL to file name. ****************************************************************************/ -int inherit_access_acl(connection_struct *conn, const char *name, mode_t mode) +int inherit_access_acl(connection_struct *conn, const char *inherit_from_dir, + const char *name, mode_t mode) { - pstring dirname; - pstrcpy(dirname, parent_dirname(name)); - - if (!lp_inherit_perms(SNUM(conn)) || directory_has_default_acl(conn, dirname)) + if (directory_has_default_acl(conn, inherit_from_dir)) return 0; - return copy_access_acl(conn, dirname, name, mode); + return copy_access_acl(conn, inherit_from_dir, name, mode); } /**************************************************************************** diff --git a/source/smbd/reply.c b/source/smbd/reply.c index a9c6aa947fc..04caa4b2284 100644 --- a/source/smbd/reply.c +++ b/source/smbd/reply.c @@ -3745,47 +3745,6 @@ int reply_printwrite(connection_struct *conn, char *inbuf,char *outbuf, int dum_ } /**************************************************************************** - The guts of the mkdir command, split out so it may be called by the NT SMB - code. -****************************************************************************/ - -NTSTATUS mkdir_internal(connection_struct *conn, const pstring directory, BOOL bad_path) -{ - int ret= -1; - - if(!CAN_WRITE(conn)) { - DEBUG(5,("mkdir_internal: failing create on read-only share %s\n", lp_servicename(SNUM(conn)))); - errno = EACCES; - return map_nt_error_from_unix(errno); - } - - if (bad_path) { - return NT_STATUS_OBJECT_PATH_NOT_FOUND; - } - - if (!check_name(directory, conn)) { - if(errno == ENOENT) { - if (bad_path) { - return NT_STATUS_OBJECT_PATH_NOT_FOUND; - } else { - return NT_STATUS_OBJECT_NAME_NOT_FOUND; - } - } - return map_nt_error_from_unix(errno); - } - - ret = vfs_MkDir(conn,directory,unix_mode(conn,aDIR,directory,True)); - if (ret == -1) { - if(errno == ENOENT) { - return NT_STATUS_OBJECT_NAME_NOT_FOUND; - } - return map_nt_error_from_unix(errno); - } - - return NT_STATUS_OK; -} - -/**************************************************************************** Reply to a mkdir. ****************************************************************************/ @@ -3796,7 +3755,6 @@ int reply_mkdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, NTSTATUS status; BOOL bad_path = False; SMB_STRUCT_STAT sbuf; - files_struct *fsp; START_PROFILE(SMBmkdir); @@ -3810,17 +3768,15 @@ int reply_mkdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, unix_convert(directory,conn,0,&bad_path,&sbuf); - 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); + status = create_directory(conn, directory); - DEBUG(5, ("open_directory returned %s\n", nt_errstr(status))); + DEBUG(5, ("create_directory returned %s\n", nt_errstr(status))); if (!NT_STATUS_IS_OK(status)) { - if (NT_STATUS_EQUAL( - status, NT_STATUS_DOS(ERRDOS, ERRfilexists))) { + if (!use_nt_status() + && NT_STATUS_EQUAL(status, + NT_STATUS_OBJECT_NAME_COLLISION)) { /* * Yes, in the DOS error code case we get a * ERRDOS:ERRnoaccess here. See BASE-SAMBA3ERROR @@ -3833,8 +3789,6 @@ int reply_mkdir(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, return ERROR_NT(status); } - close_file(fsp, NORMAL_CLOSE); - outsize = set_message(outbuf,0,0,False); DEBUG( 3, ( "mkdir %s ret=%d\n", directory, outsize ) ); @@ -3906,7 +3860,7 @@ static BOOL recursive_rmdir(connection_struct *conn, char *directory) The internals of the rmdir code - called elsewhere. ****************************************************************************/ -BOOL rmdir_internals(connection_struct *conn, char *directory) +BOOL rmdir_internals(connection_struct *conn, const char *directory) { BOOL ok; SMB_STRUCT_STAT st; @@ -3979,10 +3933,13 @@ BOOL rmdir_internals(connection_struct *conn, char *directory) } } - if (!ok) - DEBUG(3,("rmdir_internals: couldn't remove directory %s : %s\n", directory,strerror(errno))); + if (!ok) { + DEBUG(3,("rmdir_internals: couldn't remove directory %s : " + "%s\n", directory,strerror(errno))); + return False; + } - return ok; + return True; } /**************************************************************************** diff --git a/source/smbd/trans2.c b/source/smbd/trans2.c index c10662ab616..1805d67caf7 100644 --- a/source/smbd/trans2.c +++ b/source/smbd/trans2.c @@ -893,7 +893,7 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, i } } - if (total_data && smb_action == FILE_WAS_CREATED) { + if (ea_list && smb_action == FILE_WAS_CREATED) { status = set_ea(conn, fsp, fname, ea_list); if (!NT_STATUS_IS_OK(status)) { close_file(fsp,ERROR_CLOSE); @@ -4323,7 +4323,11 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", if (SMB_VFS_MKNOD(conn,fname, unixmode, dev) != 0) return(UNIXERROR(ERRDOS,ERRnoaccess)); - inherit_access_acl(conn, fname, unixmode); + if (lp_inherit_perms(SNUM(conn))) { + inherit_access_acl( + conn, parent_dirname(fname), + fname, unixmode); + } SSVAL(params,0,0); send_trans2_replies(outbuf, bufsize, params, 2, *ppdata, 0, max_data_bytes); @@ -4799,11 +4803,11 @@ static int call_trans2mkdir(connection_struct *conn, char *inbuf, char *outbuf, char *params = *pparams; char *pdata = *ppdata; pstring directory; - int ret = -1; SMB_STRUCT_STAT sbuf; BOOL bad_path = False; NTSTATUS status = NT_STATUS_OK; struct ea_list *ea_list = NULL; + files_struct *fsp; if (!CAN_WRITE(conn)) return ERROR_DOS(ERRSRV,ERRaccess); @@ -4855,15 +4859,30 @@ static int call_trans2mkdir(connection_struct *conn, char *inbuf, char *outbuf, return ERROR_NT(NT_STATUS_INVALID_PARAMETER); } - if (check_name(directory,conn)) { - ret = vfs_MkDir(conn,directory,unix_mode(conn,aDIR,directory,True)); - } - - if(ret < 0) { + if (!check_name(directory,conn)) { DEBUG(5,("call_trans2mkdir error (%s)\n", strerror(errno))); - return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess); + return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, + ERRnoaccess); + } + + status = open_directory(conn, directory, &sbuf, + FILE_READ_ATTRIBUTES, /* A stat open */ + FILE_SHARE_NONE, /* Ignored */ + FILE_CREATE, 0, NULL, &fsp); + + if (!NT_STATUS_IS_OK(status)) { +#if 0 + /* Do we need to do this here ? Need smbtorture test. JRA. */ + if (!use_nt_status() && NT_STATUS_EQUAL( + status, NT_STATUS_OBJECT_NAME_COLLISION)) { + status = NT_STATUS_DOS(ERRDOS, ERRfilexists); + } +#endif + return ERROR_NT(status); } + close_file(fsp, NORMAL_CLOSE); + /* Try and set any given EA. */ if (ea_list) { status = set_ea(conn, NULL, directory, ea_list); diff --git a/source/smbd/vfs.c b/source/smbd/vfs.c index 3ed56d3bf6f..a4ecff921af 100644 --- a/source/smbd/vfs.c +++ b/source/smbd/vfs.c @@ -307,31 +307,6 @@ BOOL vfs_directory_exist(connection_struct *conn, const char *dname, SMB_STRUCT_ } /******************************************************************* - vfs mkdir wrapper -********************************************************************/ - -int vfs_MkDir(connection_struct *conn, const char *name, mode_t mode) -{ - int ret; - SMB_STRUCT_STAT sbuf; - - if(!(ret=SMB_VFS_MKDIR(conn, name, mode))) { - - inherit_access_acl(conn, 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) && - !SMB_VFS_STAT(conn,name,&sbuf) && (mode & ~sbuf.st_mode)) - SMB_VFS_CHMOD(conn,name,sbuf.st_mode | (mode & ~sbuf.st_mode)); - } - return ret; -} - -/******************************************************************* Check if an object exists in the vfs. ********************************************************************/ |