X-Git-Url: http://git.samba.org/samba.git/?p=kai%2Fsamba.git;a=blobdiff_plain;f=source3%2Fsmbd%2Fopen.c;h=8882e5438b8edbf469fad6e298d07f53ef733278;hp=036e3e12a2db4ea9dbf162d0db9ed45f71a6891e;hb=a5651848b26719b7f9c06fbc996a369a5d97461d;hpb=670909cb07e38a06bf5db12342b3b1189f0e1ab7 diff --git a/source3/smbd/open.c b/source3/smbd/open.c index 036e3e12a2d..8882e5438b8 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -22,8 +22,6 @@ #include "includes.h" extern const struct generic_mapping file_generic_mapping; -extern struct current_user current_user; -extern userdom_struct current_user_info; extern bool global_client_failed_oplock_break; struct deferred_open_record { @@ -31,6 +29,73 @@ struct deferred_open_record { struct file_id id; }; +static NTSTATUS create_file_unixpath(connection_struct *conn, + struct smb_request *req, + const char *fname, + uint32_t access_mask, + uint32_t share_access, + uint32_t create_disposition, + uint32_t create_options, + uint32_t file_attributes, + uint32_t oplock_request, + uint64_t allocation_size, + struct security_descriptor *sd, + struct ea_list *ea_list, + + files_struct **result, + int *pinfo, + SMB_STRUCT_STAT *psbuf); + +/**************************************************************************** + SMB1 file varient of se_access_check. Never test FILE_READ_ATTRIBUTES. +****************************************************************************/ + +NTSTATUS smb1_file_se_access_check(const struct security_descriptor *sd, + const NT_USER_TOKEN *token, + uint32_t access_desired, + uint32_t *access_granted) +{ + return se_access_check(sd, + token, + (access_desired & ~FILE_READ_ATTRIBUTES), + access_granted); +} + +/**************************************************************************** + Check if we have open rights. +****************************************************************************/ + +static NTSTATUS check_open_rights(struct connection_struct *conn, + const char *fname, + uint32_t access_mask) +{ + /* Check if we have rights to open. */ + NTSTATUS status; + uint32_t access_granted = 0; + struct security_descriptor *sd; + + status = SMB_VFS_GET_NT_ACL(conn, fname, + (OWNER_SECURITY_INFORMATION | + GROUP_SECURITY_INFORMATION | + DACL_SECURITY_INFORMATION),&sd); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("check_open_rights: Could not get acl " + "on %s: %s\n", + fname, + nt_errstr(status))); + return status; + } + + status = smb1_file_se_access_check(sd, + conn->server_info->ptok, + access_mask, + &access_granted); + + TALLOC_FREE(sd); + return status; +} + /**************************************************************************** fd support routines - attempt to do a dos_open. ****************************************************************************/ @@ -72,13 +137,21 @@ static NTSTATUS fd_open(struct connection_struct *conn, NTSTATUS fd_close(files_struct *fsp) { + int ret; + if (fsp->fh->fd == -1) { return NT_STATUS_OK; /* What we used to call a stat open. */ } if (fsp->fh->ref_count > 1) { return NT_STATUS_OK; /* Shared handle. Only close last reference. */ } - return fd_close_posix(fsp); + + ret = SMB_VFS_CLOSE(fsp); + fsp->fh->fd = -1; + if (ret == -1) { + return map_nt_error_from_unix(errno); + } + return NT_STATUS_OK; } /**************************************************************************** @@ -86,7 +159,7 @@ NTSTATUS fd_close(files_struct *fsp) Do this by fd if possible. ****************************************************************************/ -static void change_file_owner_to_parent(connection_struct *conn, +void change_file_owner_to_parent(connection_struct *conn, const char *inherit_from_dir, files_struct *fsp) { @@ -117,7 +190,7 @@ static void change_file_owner_to_parent(connection_struct *conn, (unsigned int)parent_st.st_uid )); } -static NTSTATUS change_dir_owner_to_parent(connection_struct *conn, +NTSTATUS change_dir_owner_to_parent(connection_struct *conn, const char *inherit_from_dir, const char *fname, SMB_STRUCT_STAT *psbuf) @@ -125,7 +198,7 @@ static NTSTATUS change_dir_owner_to_parent(connection_struct *conn, char *saved_dir = NULL; SMB_STRUCT_STAT sbuf; SMB_STRUCT_STAT parent_st; - TALLOC_CTX *ctx = talloc_stackframe(); + TALLOC_CTX *ctx = talloc_tos(); NTSTATUS status = NT_STATUS_OK; int ret; @@ -135,7 +208,6 @@ static NTSTATUS change_dir_owner_to_parent(connection_struct *conn, DEBUG(0,("change_dir_owner_to_parent: failed to stat parent " "directory %s. Error was %s\n", inherit_from_dir, strerror(errno) )); - TALLOC_FREE(ctx); return status; } @@ -152,7 +224,6 @@ static NTSTATUS change_dir_owner_to_parent(connection_struct *conn, DEBUG(0,("change_dir_owner_to_parent: failed to get " "current working directory. Error was %s\n", strerror(errno))); - TALLOC_FREE(ctx); return status; } @@ -202,7 +273,6 @@ static NTSTATUS change_dir_owner_to_parent(connection_struct *conn, out: - TALLOC_FREE(ctx); vfs_ChDir(conn,saved_dir); return status; } @@ -279,6 +349,7 @@ static NTSTATUS open_file(files_struct *fsp, if ((open_access_mask & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) || (!file_existed && (local_flags & O_CREAT)) || ((local_flags & O_TRUNC) == O_TRUNC) ) { + const char *wild; /* * We can't actually truncate here as the file may be locked. @@ -300,8 +371,17 @@ static NTSTATUS open_file(files_struct *fsp, #endif /* Don't create files with Microsoft wildcard characters. */ + if (fsp->base_fsp) { + /* + * wildcard characters are allowed in stream names + * only test the basefilename + */ + wild = fsp->base_fsp->fsp_name; + } else { + wild = path; + } if ((local_flags & O_CREAT) && !file_existed && - ms_has_wild(path)) { + ms_has_wild(wild)) { return NT_STATUS_OBJECT_NAME_INVALID; } @@ -318,7 +398,7 @@ static NTSTATUS open_file(files_struct *fsp, /* Inherit the ACL if required */ if (lp_inherit_perms(SNUM(conn))) { - inherit_access_acl(conn, parent_dir, path, + inherit_access_posix_acl(conn, parent_dir, path, unx_mode); } @@ -334,6 +414,17 @@ static NTSTATUS open_file(files_struct *fsp, } else { fsp->fh->fd = -1; /* What we used to call a stat open. */ + if (file_existed) { + status = check_open_rights(conn, + path, + access_mask); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("open_file: Access denied on " + "file %s\n", + path)); + return status; + } + } } if (!file_existed) { @@ -386,7 +477,6 @@ static NTSTATUS open_file(files_struct *fsp, fsp->modified = False; fsp->sent_oplock_break = NO_BREAK_SENT; fsp->is_directory = False; - fsp->is_stat = False; if (conn->aio_write_behind_list && is_in_path(path, conn->aio_write_behind_list, conn->case_sensitive)) { fsp->aio_write_behind = True; @@ -396,10 +486,10 @@ static NTSTATUS open_file(files_struct *fsp, fsp->wcp = NULL; /* Write cache pointer. */ DEBUG(2,("%s opened file %s read=%s write=%s (numopen=%d)\n", - *current_user_info.smb_name ? - current_user_info.smb_name : conn->user,fsp->fsp_name, + conn->server_info->unix_name, + fsp->fsp_name, BOOLSTR(fsp->can_read), BOOLSTR(fsp->can_write), - conn->num_files_open + 1)); + conn->num_files_open)); errno = 0; return NT_STATUS_OK; @@ -409,7 +499,7 @@ static NTSTATUS open_file(files_struct *fsp, Return True if the filename is one of the special executable types. ********************************************************************/ -static bool is_executable(const char *fname) +bool is_executable(const char *fname) { if ((fname = strrchr_m(fname,'.'))) { if (strequal(fname,".com") || @@ -571,7 +661,7 @@ static void validate_my_share_entries(int num, } #endif -static bool is_stat_open(uint32 access_mask) +bool is_stat_open(uint32 access_mask) { return (access_mask && ((access_mask & ~(SYNCHRONIZE_ACCESS| FILE_READ_ATTRIBUTES| @@ -601,12 +691,6 @@ static NTSTATUS open_mode_check(connection_struct *conn, } *file_existed = True; - - if (is_stat_open(access_mask)) { - /* Stat open that doesn't trigger oplock breaks or share mode - * checks... ! JRA. */ - return NT_STATUS_OK; - } /* A delete on close prohibits everything */ @@ -614,6 +698,12 @@ static NTSTATUS open_mode_check(connection_struct *conn, return NT_STATUS_DELETE_PENDING; } + if (is_stat_open(access_mask)) { + /* Stat open that doesn't trigger oplock breaks or share mode + * checks... ! JRA. */ + return NT_STATUS_OK; + } + /* * Check if the share modes will give us access. */ @@ -772,8 +862,8 @@ static bool delay_for_oplocks(struct share_mode_lock *lck, return True; } -static bool request_timed_out(struct timeval request_time, - struct timeval timeout) +bool request_timed_out(struct timeval request_time, + struct timeval timeout) { struct timeval now, end_time; GetTimeOfDay(&now); @@ -838,13 +928,13 @@ static void defer_open(struct share_mode_lock *lck, On overwrite open ensure that the attributes match. ****************************************************************************/ -static bool open_match_attributes(connection_struct *conn, - const char *path, - uint32 old_dos_attr, - uint32 new_dos_attr, - mode_t existing_unx_mode, - mode_t new_unx_mode, - mode_t *returned_unx_mode) +bool open_match_attributes(connection_struct *conn, + const char *path, + uint32 old_dos_attr, + uint32 new_dos_attr, + mode_t existing_unx_mode, + mode_t new_unx_mode, + mode_t *returned_unx_mode) { uint32 noarch_old_dos_attr, noarch_new_dos_attr; @@ -888,8 +978,10 @@ static bool open_match_attributes(connection_struct *conn, Try and find a duplicated file handle. ****************************************************************************/ -static files_struct *fcb_or_dos_open(connection_struct *conn, - const char *fname, +NTSTATUS fcb_or_dos_open(struct smb_request *req, + connection_struct *conn, + files_struct *fsp_to_dup_into, + const char *fname, struct file_id id, uint16 file_pid, uint16 vuid, @@ -898,7 +990,6 @@ static files_struct *fcb_or_dos_open(connection_struct *conn, uint32 create_options) { files_struct *fsp; - files_struct *dup_fsp; DEBUG(5,("fcb_or_dos_open: attempting old open semantics for " "file %s.\n", fname )); @@ -927,23 +1018,21 @@ static files_struct *fcb_or_dos_open(connection_struct *conn, } if (!fsp) { - return NULL; + return NT_STATUS_NOT_FOUND; } /* quite an insane set of semantics ... */ if (is_executable(fname) && (fsp->fh->private_options & NTCREATEX_OPTIONS_PRIVATE_DENY_DOS)) { DEBUG(10,("fcb_or_dos_open: file fail due to is_executable.\n")); - return NULL; + return NT_STATUS_INVALID_PARAMETER; } /* We need to duplicate this fsp. */ - if (!NT_STATUS_IS_OK(dup_file_fsp(fsp, access_mask, share_access, - create_options, &dup_fsp))) { - return NULL; - } + dup_file_fsp(req, fsp, access_mask, share_access, + create_options, fsp_to_dup_into); - return dup_fsp; + return NT_STATUS_OK; } /**************************************************************************** @@ -1123,10 +1212,72 @@ static void schedule_defer_open(struct share_mode_lock *lck, } /**************************************************************************** - Open a file with a share mode. + Work out what access_mask to use from what the client sent us. ****************************************************************************/ -NTSTATUS open_file_ntcreate(connection_struct *conn, +static NTSTATUS calculate_access_mask(connection_struct *conn, + const char *fname, + bool file_existed, + uint32_t access_mask, + uint32_t *access_mask_out) +{ + NTSTATUS status; + + /* + * Convert GENERIC bits to specific bits. + */ + + se_map_generic(&access_mask, &file_generic_mapping); + + /* Calculate MAXIMUM_ALLOWED_ACCESS if requested. */ + if (access_mask & MAXIMUM_ALLOWED_ACCESS) { + if (file_existed) { + + struct security_descriptor *sd; + uint32_t access_granted = 0; + + status = SMB_VFS_GET_NT_ACL(conn, fname, + (OWNER_SECURITY_INFORMATION | + GROUP_SECURITY_INFORMATION | + DACL_SECURITY_INFORMATION),&sd); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("calculate_access_mask: Could not get acl " + "on file %s: %s\n", + fname, + nt_errstr(status))); + return NT_STATUS_ACCESS_DENIED; + } + + status = smb1_file_se_access_check(sd, + conn->server_info->ptok, + access_mask, + &access_granted); + + TALLOC_FREE(sd); + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("calculate_access_mask: Access denied on " + "file %s: when calculating maximum access\n", + fname)); + return NT_STATUS_ACCESS_DENIED; + } + + access_mask = access_granted; + } else { + access_mask = FILE_GENERIC_ALL; + } + } + + *access_mask_out = access_mask; + return NT_STATUS_OK; +} + +/**************************************************************************** + Open a file with a share mode. Passed in an already created files_struct *. +****************************************************************************/ + +static NTSTATUS open_file_ntcreate(connection_struct *conn, struct smb_request *req, const char *fname, SMB_STRUCT_STAT *psbuf, @@ -1138,7 +1289,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, int oplock_request, /* internal Samba oplock codes. */ /* Information (FILE_EXISTS etc.) */ int *pinfo, - files_struct **result) + files_struct *fsp) { int flags=0; int flags2=0; @@ -1148,7 +1299,6 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, bool new_file_created = False; struct file_id id; NTSTATUS fsp_open = NT_STATUS_ACCESS_DENIED; - files_struct *fsp = NULL; mode_t new_unx_mode = (mode_t)0; mode_t unx_mode = (mode_t)0; int info; @@ -1165,7 +1315,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, ZERO_STRUCT(id); if (conn->printer) { - /* + /* * Printers are handled completely differently. * Most of the passed parameters are ignored. */ @@ -1176,7 +1326,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, DEBUG(10, ("open_file_ntcreate: printer open fname=%s\n", fname)); - return print_fsp_open(conn, fname, result); + return print_fsp_open(req, conn, fname, req->vuid, fsp); } if (!parent_dirname_talloc(talloc_tos(), fname, &parent_dir, @@ -1224,7 +1374,8 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, request_time = pml->request_time; /* Remove the deferred open entry under lock. */ - lck = get_share_mode_lock(NULL, state->id, NULL, NULL); + lck = get_share_mode_lock(talloc_tos(), state->id, NULL, NULL, + NULL); if (lck == NULL) { DEBUG(0, ("could not get share mode lock\n")); } else { @@ -1239,7 +1390,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, status = check_name(conn, fname); if (!NT_STATUS_IS_OK(status)) { return status; - } + } if (!posix_open) { new_dos_attributes &= SAMBA_ATTRIBUTES_MASK; @@ -1357,19 +1508,20 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, } } - /* This is a nasty hack - must fix... JRA. */ - if (access_mask == MAXIMUM_ALLOWED_ACCESS) { - open_access_mask = access_mask = FILE_GENERIC_ALL; + status = calculate_access_mask(conn, fname, file_existed, + access_mask, + &access_mask); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("open_file_ntcreate: calculate_access_mask " + "on file %s returned %s\n", + fname, + nt_errstr(status))); + return status; } - /* - * Convert GENERIC bits to specific bits. - */ - - se_map_generic(&access_mask, &file_generic_mapping); open_access_mask = access_mask; - if (flags2 & O_TRUNC) { + if ((flags2 & O_TRUNC) || (oplock_request & FORCE_OPLOCK_BREAK_TO_NONE)) { open_access_mask |= FILE_WRITE_DATA; /* This will cause oplock breaks. */ } @@ -1381,7 +1533,8 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, * mean the same thing under DOS and Unix. */ - if (access_mask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) { + if ((access_mask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) || + (oplock_request & FORCE_OPLOCK_BREAK_TO_NONE)) { /* DENY_DOS opens are always underlying read-write on the file handle, no matter what the requested access mask says. */ @@ -1404,7 +1557,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, flags2 |= O_SYNC; } #endif /* O_SYNC */ - + if (posix_open && (access_mask & FILE_APPEND_DATA)) { flags2 |= O_APPEND; } @@ -1431,11 +1584,6 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, return NT_STATUS_ACCESS_DENIED; } - status = file_new(conn, &fsp); - if(!NT_STATUS_IS_OK(status)) { - return status; - } - fsp->file_id = vfs_file_id_from_sbuf(conn, psbuf); fsp->share_access = share_access; fsp->fh->private_options = create_options; @@ -1452,14 +1600,14 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, } if (file_existed) { + struct timespec old_write_time = get_mtimespec(psbuf); id = vfs_file_id_from_sbuf(conn, psbuf); - lck = get_share_mode_lock(NULL, id, + lck = get_share_mode_lock(talloc_tos(), id, conn->connectpath, - fname); + fname, &old_write_time); if (lck == NULL) { - file_free(fsp); DEBUG(0, ("Could not get share mode lock\n")); return NT_STATUS_SHARING_VIOLATION; } @@ -1470,7 +1618,6 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, oplock_request)) { schedule_defer_open(lck, request_time, req); TALLOC_FREE(lck); - file_free(fsp); return NT_STATUS_SHARING_VIOLATION; } @@ -1490,7 +1637,6 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, oplock_request)) { schedule_defer_open(lck, request_time, req); TALLOC_FREE(lck); - file_free(fsp); return NT_STATUS_SHARING_VIOLATION; } } @@ -1498,7 +1644,6 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, if (NT_STATUS_EQUAL(status, NT_STATUS_DELETE_PENDING)) { /* DELETE_PENDING is not deferred for a second */ TALLOC_FREE(lck); - file_free(fsp); return status; } @@ -1513,33 +1658,31 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, if (create_options & (NTCREATEX_OPTIONS_PRIVATE_DENY_DOS| NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) { - files_struct *fsp_dup; - if (req == NULL) { DEBUG(0, ("DOS open without an SMB " "request!\n")); TALLOC_FREE(lck); - file_free(fsp); return NT_STATUS_INTERNAL_ERROR; } /* Use the client requested access mask here, * not the one we open with. */ - fsp_dup = fcb_or_dos_open(conn, fname, id, - req->smbpid, - req->vuid, - access_mask, - share_access, - create_options); - - if (fsp_dup) { + status = fcb_or_dos_open(req, + conn, + fsp, + fname, + id, + req->smbpid, + req->vuid, + access_mask, + share_access, + create_options); + + if (NT_STATUS_IS_OK(status)) { TALLOC_FREE(lck); - file_free(fsp); if (pinfo) { *pinfo = FILE_WAS_OPENED; } - conn->num_files_open++; - *result = fsp_dup; return NT_STATUS_OK; } } @@ -1563,11 +1706,11 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, } if (((can_access_mask & FILE_WRITE_DATA) && !CAN_WRITE(conn)) || - !can_access_file(conn,fname,psbuf,can_access_mask)) { + !can_access_file_data(conn,fname,psbuf,can_access_mask)) { can_access = False; } - /* + /* * If we're returning a share violation, ensure we * cope with the braindead 1 second delay. */ @@ -1620,7 +1763,6 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, } else { status = NT_STATUS_ACCESS_DENIED; } - file_free(fsp); return status; } @@ -1658,12 +1800,11 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, if (lck != NULL) { TALLOC_FREE(lck); } - file_free(fsp); return fsp_open; } if (!file_existed) { - + struct timespec old_write_time = get_mtimespec(psbuf); /* * Deal with the race condition where two smbd's detect the * file doesn't exist and do the create at the same time. One @@ -1681,15 +1822,14 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, id = fsp->file_id; - lck = get_share_mode_lock(NULL, id, + lck = get_share_mode_lock(talloc_tos(), id, conn->connectpath, - fname); + fname, &old_write_time); if (lck == NULL) { DEBUG(0, ("open_file_ntcreate: Could not get share " "mode lock for %s\n", fname)); fd_close(fsp); - file_free(fsp); return NT_STATUS_SHARING_VIOLATION; } @@ -1700,7 +1840,6 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, schedule_defer_open(lck, request_time, req); TALLOC_FREE(lck); fd_close(fsp); - file_free(fsp); return NT_STATUS_SHARING_VIOLATION; } @@ -1719,7 +1858,6 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, schedule_defer_open(lck, request_time, req); TALLOC_FREE(lck); fd_close(fsp); - file_free(fsp); return NT_STATUS_SHARING_VIOLATION; } } @@ -1728,7 +1866,6 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, struct deferred_open_record state; fd_close(fsp); - file_free(fsp); state.delayed_for_oplocks = False; state.id = id; @@ -1764,14 +1901,15 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, the kernel refuses the operations then the kernel is wrong. note that GPFS supports it as well - jmcd */ - ret_flock = SMB_VFS_KERNEL_FLOCK(fsp, fsp->fh->fd, share_access); - if(ret_flock == -1 ){ + if (fsp->fh->fd != -1) { + ret_flock = SMB_VFS_KERNEL_FLOCK(fsp, share_access); + if(ret_flock == -1 ){ - TALLOC_FREE(lck); - fd_close(fsp); - file_free(fsp); - - return NT_STATUS_SHARING_VIOLATION; + TALLOC_FREE(lck); + fd_close(fsp); + + return NT_STATUS_SHARING_VIOLATION; + } } /* @@ -1789,12 +1927,11 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, * We are modifing the file after open - update the stat * struct.. */ - if ((SMB_VFS_FTRUNCATE(fsp,fsp->fh->fd,0) == -1) || + if ((SMB_VFS_FTRUNCATE(fsp, 0) == -1) || (SMB_VFS_FSTAT(fsp, psbuf)==-1)) { status = map_nt_error_from_unix(errno); TALLOC_FREE(lck); fd_close(fsp); - file_free(fsp); return status; } } @@ -1802,7 +1939,10 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, /* Record the options we were opened with. */ fsp->share_access = share_access; fsp->fh->private_options = create_options; - fsp->access_mask = access_mask; + /* + * According to Samba4, SEC_FILE_READ_ATTRIBUTE is always granted, + */ + fsp->access_mask = access_mask | FILE_READ_ATTRIBUTES; if (file_existed) { /* stat opens on existing files don't get oplocks. */ @@ -1823,7 +1963,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, *pinfo = info; } - /* + /* * Setup the oplock info in both the shared memory and * file structs. */ @@ -1840,10 +1980,12 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, new_file_created = True; } - set_share_mode(lck, fsp, current_user.ut.uid, 0, fsp->oplock_type, new_file_created); + set_share_mode(lck, fsp, conn->server_info->utok.uid, 0, + fsp->oplock_type, new_file_created); /* Handle strange delete on close create semantics. */ - if ((create_options & FILE_DELETE_ON_CLOSE) && can_set_initial_delete_on_close(lck)) { + if (create_options & FILE_DELETE_ON_CLOSE) { + status = can_set_delete_on_close(fsp, True, new_dos_attributes); if (!NT_STATUS_IS_OK(status)) { @@ -1851,14 +1993,13 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, del_share_mode(lck, fsp); TALLOC_FREE(lck); fd_close(fsp); - file_free(fsp); return status; } /* Note that here we set the *inital* delete on close flag, not the regular one. The magic gets handled in close. */ fsp->initial_delete_on_close = True; } - + if (new_file_created) { /* Files should be initially set as archive */ if (lp_map_archive(SNUM(conn)) || @@ -1887,7 +2028,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, int saved_errno = errno; /* We might get ENOSYS in the next * call.. */ - if (SMB_VFS_FCHMOD_ACL(fsp, fsp->fh->fd, unx_mode) == -1 && + if (SMB_VFS_FCHMOD_ACL(fsp, unx_mode) == -1 && errno == ENOSYS) { errno = saved_errno; /* Ignore ENOSYS */ } @@ -1901,8 +2042,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, { int saved_errno = errno; /* We might get ENOSYS in the * next call.. */ - ret = SMB_VFS_FCHMOD_ACL(fsp, fsp->fh->fd, - new_unx_mode); + ret = SMB_VFS_FCHMOD_ACL(fsp, new_unx_mode); if (ret == -1 && errno == ENOSYS) { errno = saved_errno; /* Ignore ENOSYS */ @@ -1928,17 +2068,16 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, } TALLOC_FREE(lck); - conn->num_files_open++; - - *result = fsp; return NT_STATUS_OK; } + /**************************************************************************** Open a file for for write to ensure that we can fchmod it. ****************************************************************************/ -NTSTATUS open_file_fchmod(connection_struct *conn, const char *fname, +NTSTATUS open_file_fchmod(struct smb_request *req, connection_struct *conn, + const char *fname, SMB_STRUCT_STAT *psbuf, files_struct **result) { files_struct *fsp = NULL; @@ -1948,24 +2087,38 @@ NTSTATUS open_file_fchmod(connection_struct *conn, const char *fname, return NT_STATUS_INVALID_PARAMETER; } - status = file_new(conn, &fsp); + status = file_new(req, conn, &fsp); if(!NT_STATUS_IS_OK(status)) { return status; } - /* 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, NULL, NULL, NULL, fname, psbuf, O_WRONLY, - 0, FILE_WRITE_DATA, FILE_WRITE_DATA); + status = SMB_VFS_CREATE_FILE( + conn, /* conn */ + NULL, /* req */ + 0, /* root_dir_fid */ + fname, /* fname */ + 0, /* create_file_flags */ + FILE_WRITE_DATA, /* access_mask */ + (FILE_SHARE_READ | FILE_SHARE_WRITE | /* share_access */ + FILE_SHARE_DELETE), + FILE_OPEN, /* create_disposition*/ + 0, /* create_options */ + 0, /* file_attributes */ + 0, /* oplock_request */ + 0, /* allocation_size */ + NULL, /* sd */ + NULL, /* ea_list */ + &fsp, /* result */ + NULL, /* pinfo */ + psbuf); /* psbuf */ - /* + /* * This is not a user visible file open. - * Don't set a share mode and don't increment - * the conn->num_files_open. + * Don't set a share mode. */ if (!NT_STATUS_IS_OK(status)) { - file_free(fsp); + file_free(req, fsp); return status; } @@ -1977,10 +2130,10 @@ NTSTATUS open_file_fchmod(connection_struct *conn, const char *fname, Close the fchmod file fd - ensure no locks are lost. ****************************************************************************/ -NTSTATUS close_file_fchmod(files_struct *fsp) +NTSTATUS close_file_fchmod(struct smb_request *req, files_struct *fsp) { NTSTATUS status = fd_close(fsp); - file_free(fsp); + file_free(req, fsp); return status; } @@ -2047,7 +2200,7 @@ static NTSTATUS mkdir_internal(connection_struct *conn, } if (lp_inherit_perms(SNUM(conn))) { - inherit_access_acl(conn, parent_dir, name, mode); + inherit_access_posix_acl(conn, parent_dir, name, mode); } if (!(file_attributes & FILE_FLAG_POSIX_SEMANTICS)) { @@ -2078,22 +2231,23 @@ static NTSTATUS mkdir_internal(connection_struct *conn, Open a directory from an NT SMB call. ****************************************************************************/ -NTSTATUS open_directory(connection_struct *conn, - struct smb_request *req, - const char *fname, - SMB_STRUCT_STAT *psbuf, - uint32 access_mask, - uint32 share_access, - uint32 create_disposition, - uint32 create_options, - uint32 file_attributes, - int *pinfo, - files_struct **result) +static NTSTATUS open_directory(connection_struct *conn, + struct smb_request *req, + const char *fname, + SMB_STRUCT_STAT *psbuf, + uint32 access_mask, + uint32 share_access, + uint32 create_disposition, + uint32 create_options, + uint32 file_attributes, + int *pinfo, + files_struct **result) { files_struct *fsp = NULL; bool dir_existed = VALID_STAT(*psbuf) ? True : False; struct share_mode_lock *lck = NULL; NTSTATUS status; + struct timespec mtimespec; int info = 0; DEBUG(5,("open_directory: opening directory %s, access_mask = 0x%x, " @@ -2106,11 +2260,24 @@ NTSTATUS open_directory(connection_struct *conn, (unsigned int)create_disposition, (unsigned int)file_attributes)); - if (is_ntfs_stream_name(fname)) { + if (!(file_attributes & FILE_FLAG_POSIX_SEMANTICS) && + (conn->fs_capabilities & FILE_NAMED_STREAMS) && + is_ntfs_stream_name(fname)) { DEBUG(2, ("open_directory: %s is a stream name!\n", fname)); return NT_STATUS_NOT_A_DIRECTORY; } + status = calculate_access_mask(conn, fname, dir_existed, + access_mask, + &access_mask); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("open_directory: calculate_access_mask " + "on file %s returned %s\n", + fname, + nt_errstr(status))); + return status; + } + switch( create_disposition ) { case FILE_OPEN: @@ -2185,7 +2352,20 @@ NTSTATUS open_directory(connection_struct *conn, return NT_STATUS_NOT_A_DIRECTORY; } - status = file_new(conn, &fsp); + if (info == FILE_WAS_OPENED) { + status = check_open_rights(conn, + fname, + access_mask); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("open_directory: check_open_rights on " + "file %s failed with %s\n", + fname, + nt_errstr(status))); + return status; + } + } + + status = file_new(req, conn, &fsp); if(!NT_STATUS_IS_OK(status)) { return status; } @@ -2204,25 +2384,28 @@ NTSTATUS open_directory(connection_struct *conn, fsp->share_access = share_access; fsp->fh->private_options = create_options; - fsp->access_mask = access_mask; - + /* + * According to Samba4, SEC_FILE_READ_ATTRIBUTE is always granted, + */ + fsp->access_mask = access_mask | FILE_READ_ATTRIBUTES; fsp->print_file = False; fsp->modified = False; fsp->oplock_type = NO_OPLOCK; 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->file_id, + mtimespec = get_mtimespec(psbuf); + + lck = get_share_mode_lock(talloc_tos(), fsp->file_id, conn->connectpath, - fname); + fname, &mtimespec); if (lck == NULL) { DEBUG(0, ("open_directory: Could not get share mode lock for %s\n", fname)); - file_free(fsp); + file_free(req, fsp); return NT_STATUS_SHARING_VIOLATION; } @@ -2232,11 +2415,12 @@ NTSTATUS open_directory(connection_struct *conn, if (!NT_STATUS_IS_OK(status)) { TALLOC_FREE(lck); - file_free(fsp); + file_free(req, fsp); return status; } - set_share_mode(lck, fsp, current_user.ut.uid, 0, NO_OPLOCK, True); + set_share_mode(lck, fsp, conn->server_info->utok.uid, 0, NO_OPLOCK, + True); /* For directories the delete on close bit at open time seems always to be honored on close... See test 19 in Samba4 BASE-DELETE. */ @@ -2244,7 +2428,7 @@ NTSTATUS open_directory(connection_struct *conn, status = can_set_delete_on_close(fsp, True, 0); if (!NT_STATUS_IS_OK(status) && !NT_STATUS_EQUAL(status, NT_STATUS_DIRECTORY_NOT_EMPTY)) { TALLOC_FREE(lck); - file_free(fsp); + file_free(req, fsp); return status; } @@ -2261,8 +2445,6 @@ NTSTATUS open_directory(connection_struct *conn, *pinfo = info; } - conn->num_files_open++; - *result = fsp; return NT_STATUS_OK; } @@ -2275,74 +2457,32 @@ NTSTATUS create_directory(connection_struct *conn, struct smb_request *req, cons SET_STAT_INVALID(sbuf); - status = open_directory(conn, req, directory, &sbuf, - FILE_READ_ATTRIBUTES, /* Just a stat open */ - FILE_SHARE_NONE, /* Ignored for stat opens */ - FILE_CREATE, - 0, - FILE_ATTRIBUTE_DIRECTORY, - NULL, - &fsp); + status = SMB_VFS_CREATE_FILE( + conn, /* conn */ + req, /* req */ + 0, /* root_dir_fid */ + directory, /* fname */ + 0, /* create_file_flags */ + FILE_READ_ATTRIBUTES, /* access_mask */ + FILE_SHARE_NONE, /* share_access */ + FILE_CREATE, /* create_disposition*/ + FILE_DIRECTORY_FILE, /* create_options */ + FILE_ATTRIBUTE_DIRECTORY, /* file_attributes */ + 0, /* oplock_request */ + 0, /* allocation_size */ + NULL, /* sd */ + NULL, /* ea_list */ + &fsp, /* result */ + NULL, /* pinfo */ + &sbuf); /* psbuf */ if (NT_STATUS_IS_OK(status)) { - close_file(fsp, NORMAL_CLOSE); + close_file(req, fsp, NORMAL_CLOSE); } return status; } -/**************************************************************************** - Open a pseudo-file (no locking checks - a 'stat' open). -****************************************************************************/ - -NTSTATUS open_file_stat(connection_struct *conn, struct smb_request *req, - const char *fname, SMB_STRUCT_STAT *psbuf, - files_struct **result) -{ - files_struct *fsp = NULL; - NTSTATUS status; - - if (!VALID_STAT(*psbuf)) { - return NT_STATUS_INVALID_PARAMETER; - } - - /* Can't 'stat' open directories. */ - if(S_ISDIR(psbuf->st_mode)) { - return NT_STATUS_FILE_IS_A_DIRECTORY; - } - - status = file_new(conn, &fsp); - if(!NT_STATUS_IS_OK(status)) { - return status; - } - - DEBUG(5,("open_file_stat: 'opening' file %s\n", fname)); - - /* - * Setup the files_struct for it. - */ - - fsp->mode = psbuf->st_mode; - fsp->file_id = vfs_file_id_from_sbuf(conn, psbuf); - fsp->vuid = req ? req->vuid : UID_FIELD_INVALID; - fsp->file_pid = req ? req->smbpid : 0; - fsp->can_lock = False; - fsp->can_read = False; - fsp->can_write = False; - fsp->print_file = False; - fsp->modified = False; - fsp->oplock_type = NO_OPLOCK; - fsp->sent_oplock_break = NO_BREAK_SENT; - fsp->is_directory = False; - fsp->is_stat = True; - string_set(&fsp->fsp_name,fname); - - conn->num_files_open++; - - *result = fsp; - return NT_STATUS_OK; -} - /**************************************************************************** Receive notification that one of our open files has been renamed by another smbd process. @@ -2420,8 +2560,8 @@ static int restore_case_semantics(struct case_semantics_state *state) /**************************************************************************** Save case semantics. ****************************************************************************/ -static struct case_semantics_state *set_posix_case_semantics(TALLOC_CTX *mem_ctx, - connection_struct *conn) +struct case_semantics_state *set_posix_case_semantics(TALLOC_CTX *mem_ctx, + connection_struct *conn) { struct case_semantics_state *result; @@ -2445,29 +2585,140 @@ static struct case_semantics_state *set_posix_case_semantics(TALLOC_CTX *mem_ctx return result; } +/* + * If a main file is opened for delete, all streams need to be checked for + * !FILE_SHARE_DELETE. Do this by opening with DELETE_ACCESS. + * If that works, delete them all by setting the delete on close and close. + */ + +static NTSTATUS open_streams_for_delete(connection_struct *conn, + const char *fname) +{ + struct stream_struct *stream_info; + files_struct **streams; + int i; + unsigned int num_streams; + TALLOC_CTX *frame = talloc_stackframe(); + NTSTATUS status; + + status = SMB_VFS_STREAMINFO(conn, NULL, fname, talloc_tos(), + &num_streams, &stream_info); + + if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED) + || NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { + DEBUG(10, ("no streams around\n")); + TALLOC_FREE(frame); + return NT_STATUS_OK; + } + + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("SMB_VFS_STREAMINFO failed: %s\n", + nt_errstr(status))); + goto fail; + } + + DEBUG(10, ("open_streams_for_delete found %d streams\n", + num_streams)); + + if (num_streams == 0) { + TALLOC_FREE(frame); + return NT_STATUS_OK; + } + + streams = TALLOC_ARRAY(talloc_tos(), files_struct *, num_streams); + if (streams == NULL) { + DEBUG(0, ("talloc failed\n")); + status = NT_STATUS_NO_MEMORY; + goto fail; + } + + for (i=0; i= 0; i--) { + if (streams[i] == NULL) { + continue; + } + + DEBUG(10, ("Closing stream # %d, %s\n", i, + streams[i]->fsp_name)); + close_file(NULL, streams[i], NORMAL_CLOSE); + } + + fail: + TALLOC_FREE(frame); + return status; +} + /* * Wrapper around open_file_ntcreate and open_directory */ -NTSTATUS create_file_unixpath(connection_struct *conn, - struct smb_request *req, - const char *fname, - uint32_t access_mask, - uint32_t share_access, - uint32_t create_disposition, - uint32_t create_options, - uint32_t file_attributes, - uint32_t oplock_request, - SMB_BIG_UINT allocation_size, - struct security_descriptor *sd, - struct ea_list *ea_list, - - files_struct **result, - int *pinfo, - SMB_STRUCT_STAT *psbuf) +static NTSTATUS create_file_unixpath(connection_struct *conn, + struct smb_request *req, + const char *fname, + uint32_t access_mask, + uint32_t share_access, + uint32_t create_disposition, + uint32_t create_options, + uint32_t file_attributes, + uint32_t oplock_request, + uint64_t allocation_size, + struct security_descriptor *sd, + struct ea_list *ea_list, + + files_struct **result, + int *pinfo, + SMB_STRUCT_STAT *psbuf) { SMB_STRUCT_STAT sbuf; int info = FILE_WAS_OPENED; + files_struct *base_fsp = NULL; files_struct *fsp = NULL; NTSTATUS status; @@ -2489,6 +2740,11 @@ NTSTATUS create_file_unixpath(connection_struct *conn, goto fail; } + if (create_options & NTCREATEX_OPTIONS_INVALID_PARAM_MASK) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + if (req == NULL) { oplock_request |= INTERNAL_OPEN_ONLY; } @@ -2497,7 +2753,23 @@ NTSTATUS create_file_unixpath(connection_struct *conn, sbuf = *psbuf; } else { - SET_STAT_INVALID(sbuf); + if (SMB_VFS_STAT(conn, fname, &sbuf) == -1) { + SET_STAT_INVALID(sbuf); + } + } + + if ((conn->fs_capabilities & FILE_NAMED_STREAMS) + && (access_mask & DELETE_ACCESS) + && !is_ntfs_stream_name(fname)) { + /* + * We can't open a file with DELETE access if any of the + * streams is open without FILE_SHARE_DELETE + */ + status = open_streams_for_delete(conn, fname); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } } /* This is the correct thing to do (check every time) but can_delete @@ -2513,9 +2785,7 @@ NTSTATUS create_file_unixpath(connection_struct *conn, && (create_disposition != FILE_CREATE) && (share_access & FILE_SHARE_DELETE) && (access_mask & DELETE_ACCESS) - && (((dos_mode(conn, fname, &sbuf) & FILE_ATTRIBUTE_READONLY) - && !lp_delete_readonly(SNUM(conn))) - || !can_delete_file_in_directory(conn, fname))) { + && (!can_delete_file_in_directory(conn, fname))) { status = NT_STATUS_ACCESS_DENIED; goto fail; } @@ -2530,14 +2800,67 @@ NTSTATUS create_file_unixpath(connection_struct *conn, } #endif + if ((conn->fs_capabilities & FILE_NAMED_STREAMS) + && is_ntfs_stream_name(fname) + && (!(create_options & NTCREATEX_OPTIONS_PRIVATE_STREAM_DELETE))) { + char *base; + uint32 base_create_disposition; + + if (create_options & FILE_DIRECTORY_FILE) { + status = NT_STATUS_NOT_A_DIRECTORY; + goto fail; + } + + status = split_ntfs_stream_name(talloc_tos(), fname, + &base, NULL); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("create_file_unixpath: " + "split_ntfs_stream_name failed: %s\n", + nt_errstr(status))); + goto fail; + } + + SMB_ASSERT(!is_ntfs_stream_name(base)); /* paranoia.. */ + + switch (create_disposition) { + case FILE_OPEN: + base_create_disposition = FILE_OPEN; + break; + default: + base_create_disposition = FILE_OPEN_IF; + break; + } + + status = create_file_unixpath(conn, NULL, base, 0, + FILE_SHARE_READ + | FILE_SHARE_WRITE + | FILE_SHARE_DELETE, + base_create_disposition, + 0, 0, 0, 0, NULL, NULL, + &base_fsp, NULL, NULL); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("create_file_unixpath for base %s failed: " + "%s\n", base, nt_errstr(status))); + goto fail; + } + /* we don't need to low level fd */ + fd_close(base_fsp); + } + /* * If it's a request for a directory open, deal with it separately. */ if (create_options & FILE_DIRECTORY_FILE) { + if (create_options & FILE_NON_DIRECTORY_FILE) { + status = NT_STATUS_INVALID_PARAMETER; + goto fail; + } + /* Can't open a temp directory. IFS kit test. */ - if (file_attributes & FILE_ATTRIBUTE_TEMPORARY) { + if (!(file_attributes & FILE_FLAG_POSIX_SEMANTICS) && + (file_attributes & FILE_ATTRIBUTE_TEMPORARY)) { status = NT_STATUS_INVALID_PARAMETER; goto fail; } @@ -2559,13 +2882,46 @@ NTSTATUS create_file_unixpath(connection_struct *conn, * Ordinary file case. */ - status = open_file_ntcreate( - conn, req, fname, &sbuf, access_mask, share_access, - create_disposition, create_options, file_attributes, - oplock_request, &info, &fsp); + status = file_new(req, conn, &fsp); + if(!NT_STATUS_IS_OK(status)) { + goto fail; + } + + /* + * We're opening the stream element of a base_fsp + * we already opened. Set up the base_fsp pointer. + */ + if (base_fsp) { + fsp->base_fsp = base_fsp; + } + + status = open_file_ntcreate(conn, + req, + fname, + &sbuf, + access_mask, + share_access, + create_disposition, + create_options, + file_attributes, + oplock_request, + &info, + fsp); + + if(!NT_STATUS_IS_OK(status)) { + file_free(req, fsp); + fsp = NULL; + } if (NT_STATUS_EQUAL(status, NT_STATUS_FILE_IS_A_DIRECTORY)) { + /* A stream open never opens a directory */ + + if (base_fsp) { + status = NT_STATUS_FILE_IS_A_DIRECTORY; + goto fail; + } + /* * Fail the open if it was explicitly a non-directory * file. @@ -2589,6 +2945,8 @@ NTSTATUS create_file_unixpath(connection_struct *conn, goto fail; } + fsp->base_fsp = base_fsp; + /* * According to the MS documentation, the only time the security * descriptor is applied to the opened file is iff we *created* the @@ -2603,25 +2961,23 @@ NTSTATUS create_file_unixpath(connection_struct *conn, if ((sd != NULL) && (info == FILE_WAS_CREATED) && lp_nt_acl_support(SNUM(conn))) { - uint32_t sec_info_sent = ALL_SECURITY_INFORMATION; + uint32_t sec_info_sent; uint32_t saved_access_mask = fsp->access_mask; - if (sd->owner_sid == NULL) { - sec_info_sent &= ~OWNER_SECURITY_INFORMATION; - } - if (sd->group_sid == NULL) { - sec_info_sent &= ~GROUP_SECURITY_INFORMATION; - } - if (sd->sacl == NULL) { - sec_info_sent &= ~SACL_SECURITY_INFORMATION; - } - if (sd->dacl == NULL) { - sec_info_sent &= ~DACL_SECURITY_INFORMATION; - } + sec_info_sent = get_sec_info(sd); fsp->access_mask = FILE_GENERIC_ALL; - status = SMB_VFS_FSET_NT_ACL(fsp, sec_info_sent, sd); + /* Convert all the generic bits. */ + security_acl_map_generic(sd->dacl, &file_generic_mapping); + security_acl_map_generic(sd->sacl, &file_generic_mapping); + + if (sec_info_sent & (OWNER_SECURITY_INFORMATION| + GROUP_SECURITY_INFORMATION| + DACL_SECURITY_INFORMATION| + SACL_SECURITY_INFORMATION)) { + status = SMB_VFS_FSET_NT_ACL(fsp, sec_info_sent, sd); + } fsp->access_mask = saved_access_mask; @@ -2660,11 +3016,11 @@ NTSTATUS create_file_unixpath(connection_struct *conn, } } else { fsp->initial_allocation_size = smb_roundup( - fsp->conn, (SMB_BIG_UINT)sbuf.st_size); + fsp->conn, (uint64_t)sbuf.st_size); } } - DEBUG(10, ("create_file: info=%d\n", info)); + DEBUG(10, ("create_file_unixpath: info=%d\n", info)); *result = fsp; if (pinfo != NULL) { @@ -2681,34 +3037,135 @@ NTSTATUS create_file_unixpath(connection_struct *conn, return NT_STATUS_OK; fail: - DEBUG(10, ("create_file: %s\n", nt_errstr(status))); + DEBUG(10, ("create_file_unixpath: %s\n", nt_errstr(status))); if (fsp != NULL) { - close_file(fsp, ERROR_CLOSE); + if (base_fsp && fsp->base_fsp == base_fsp) { + /* + * The close_file below will close + * fsp->base_fsp. + */ + base_fsp = NULL; + } + close_file(req, fsp, ERROR_CLOSE); fsp = NULL; } + if (base_fsp != NULL) { + close_file(req, base_fsp, ERROR_CLOSE); + base_fsp = NULL; + } return status; } -NTSTATUS create_file(connection_struct *conn, - struct smb_request *req, - uint16_t root_dir_fid, - const char *fname, - uint32_t access_mask, - uint32_t share_access, - uint32_t create_disposition, - uint32_t create_options, - uint32_t file_attributes, - uint32_t oplock_request, - SMB_BIG_UINT allocation_size, - struct security_descriptor *sd, - struct ea_list *ea_list, - - files_struct **result, - int *pinfo, - SMB_STRUCT_STAT *psbuf) +/* + * Calculate the full path name given a relative fid. + */ +NTSTATUS get_relative_fid_filename(connection_struct *conn, + struct smb_request *req, + uint16_t root_dir_fid, + const char *fname, char **new_fname) +{ + files_struct *dir_fsp; + char *parent_fname = NULL; + + if (root_dir_fid == 0 || !fname || !new_fname || !*new_fname) { + return NT_STATUS_INTERNAL_ERROR; + } + + dir_fsp = file_fsp(req, root_dir_fid); + + if (dir_fsp == NULL) { + return NT_STATUS_INVALID_HANDLE; + } + + if (!dir_fsp->is_directory) { + + /* + * Check to see if this is a mac fork of some kind. + */ + + if ((conn->fs_capabilities & FILE_NAMED_STREAMS) && + is_ntfs_stream_name(fname)) { + return NT_STATUS_OBJECT_PATH_NOT_FOUND; + } + + /* + we need to handle the case when we get a + relative open relative to a file and the + pathname is blank - this is a reopen! + (hint from demyn plantenberg) + */ + + return NT_STATUS_INVALID_HANDLE; + } + + if (ISDOT(dir_fsp->fsp_name)) { + /* + * We're at the toplevel dir, the final file name + * must not contain ./, as this is filtered out + * normally by srvstr_get_path and unix_convert + * explicitly rejects paths containing ./. + */ + parent_fname = talloc_strdup(talloc_tos(), ""); + if (parent_fname == NULL) { + return NT_STATUS_NO_MEMORY; + } + } else { + size_t dir_name_len = strlen(dir_fsp->fsp_name); + + /* + * Copy in the base directory name. + */ + + parent_fname = TALLOC_ARRAY(talloc_tos(), char, + dir_name_len+2); + if (parent_fname == NULL) { + return NT_STATUS_NO_MEMORY; + } + memcpy(parent_fname, dir_fsp->fsp_name, + dir_name_len+1); + + /* + * Ensure it ends in a '/'. + * We used TALLOC_SIZE +2 to add space for the '/'. + */ + + if(dir_name_len + && (parent_fname[dir_name_len-1] != '\\') + && (parent_fname[dir_name_len-1] != '/')) { + parent_fname[dir_name_len] = '/'; + parent_fname[dir_name_len+1] = '\0'; + } + } + + *new_fname = talloc_asprintf(talloc_tos(), "%s%s", parent_fname, + fname); + if (*new_fname == NULL) { + return NT_STATUS_NO_MEMORY; + } + + return NT_STATUS_OK; +} + +NTSTATUS create_file_default(connection_struct *conn, + struct smb_request *req, + uint16_t root_dir_fid, + const char *fname, + uint32_t create_file_flags, + uint32_t access_mask, + uint32_t share_access, + uint32_t create_disposition, + uint32_t create_options, + uint32_t file_attributes, + uint32_t oplock_request, + uint64_t allocation_size, + struct security_descriptor *sd, + struct ea_list *ea_list, + + files_struct **result, + int *pinfo, + SMB_STRUCT_STAT *psbuf) { - TALLOC_CTX *frame = talloc_stackframe(); struct case_semantics_state *case_state = NULL; SMB_STRUCT_STAT sbuf; int info = FILE_WAS_OPENED; @@ -2720,7 +3177,7 @@ NTSTATUS create_file(connection_struct *conn, "create_disposition = 0x%x create_options = 0x%x " "oplock_request = 0x%x " "root_dir_fid = 0x%x, ea_list = 0x%p, sd = 0x%p, " - "fname = %s\n", + "create_file_flags = 0x%x, fname = %s\n", (unsigned int)access_mask, (unsigned int)file_attributes, (unsigned int)share_access, @@ -2728,106 +3185,34 @@ NTSTATUS create_file(connection_struct *conn, (unsigned int)create_options, (unsigned int)oplock_request, (unsigned int)root_dir_fid, - ea_list, sd, fname)); + ea_list, sd, create_file_flags, fname)); /* - * Get the file name. + * Calculate the filename from the root_dir_if if necessary. */ if (root_dir_fid != 0) { - /* - * This filename is relative to a directory fid. - */ - char *parent_fname = NULL; - files_struct *dir_fsp = file_fsp(root_dir_fid); - - if (dir_fsp == NULL) { - status = NT_STATUS_INVALID_HANDLE; - goto fail; - } - - if (!dir_fsp->is_directory) { + char *new_fname; - /* - * Check to see if this is a mac fork of some kind. - */ - - if (is_ntfs_stream_name(fname)) { - status = NT_STATUS_OBJECT_PATH_NOT_FOUND; - goto fail; - } - - /* - we need to handle the case when we get a - relative open relative to a file and the - pathname is blank - this is a reopen! - (hint from demyn plantenberg) - */ - - status = NT_STATUS_INVALID_HANDLE; + status = get_relative_fid_filename(conn, req, root_dir_fid, + fname, &new_fname); + if (!NT_STATUS_IS_OK(status)) { goto fail; } - if (ISDOT(dir_fsp->fsp_name)) { - /* - * We're at the toplevel dir, the final file name - * must not contain ./, as this is filtered out - * normally by srvstr_get_path and unix_convert - * explicitly rejects paths containing ./. - */ - parent_fname = talloc_strdup(talloc_tos(), ""); - if (parent_fname == NULL) { - status = NT_STATUS_NO_MEMORY; - goto fail; - } - } else { - size_t dir_name_len = strlen(dir_fsp->fsp_name); - - /* - * Copy in the base directory name. - */ - - parent_fname = TALLOC_ARRAY(talloc_tos(), char, - dir_name_len+2); - if (parent_fname == NULL) { - status = NT_STATUS_NO_MEMORY; - goto fail; - } - memcpy(parent_fname, dir_fsp->fsp_name, - dir_name_len+1); - - /* - * Ensure it ends in a '/'. - * We used TALLOC_SIZE +2 to add space for the '/'. - */ - - if(dir_name_len - && (parent_fname[dir_name_len-1] != '\\') - && (parent_fname[dir_name_len-1] != '/')) { - parent_fname[dir_name_len] = '/'; - parent_fname[dir_name_len+1] = '\0'; - } - } + fname = new_fname; + } - fname = talloc_asprintf(talloc_tos(), "%s%s", parent_fname, - fname); - if (fname == NULL) { - status = NT_STATUS_NO_MEMORY; - goto fail; - } - } else { - /* - * Check to see if this is a mac fork of some kind. - */ + /* + * Check to see if this is a mac fork of some kind. + */ - if (is_ntfs_stream_name(fname)) { - enum FAKE_FILE_TYPE fake_file_type; + if (is_ntfs_stream_name(fname)) { + enum FAKE_FILE_TYPE fake_file_type; - fake_file_type = is_fake_file(fname); + fake_file_type = is_fake_file(fname); - if (fake_file_type == FAKE_FILE_TYPE_NONE) { - return NT_STATUS_OBJECT_PATH_NOT_FOUND; - } + if (fake_file_type != FAKE_FILE_TYPE_NONE) { /* * Here we go! support for changing the disk quotas @@ -2840,14 +3225,21 @@ NTSTATUS create_file(connection_struct *conn, * also tries a QUERY_FILE_INFO on the file and then * close it */ - status = open_fake_file(conn, fake_file_type, fname, + status = open_fake_file(req, conn, req->vuid, + fake_file_type, fname, access_mask, &fsp); if (!NT_STATUS_IS_OK(status)) { goto fail; } + ZERO_STRUCT(sbuf); goto done; } + + if (!(conn->fs_capabilities & FILE_NAMED_STREAMS)) { + status = NT_STATUS_OBJECT_PATH_NOT_FOUND; + goto fail; + } } if ((req != NULL) && (req->flags2 & FLAGS2_DFS_PATHNAMES)) { @@ -2874,10 +3266,9 @@ NTSTATUS create_file(connection_struct *conn, if (file_attributes & FILE_FLAG_POSIX_SEMANTICS) { case_state = set_posix_case_semantics(talloc_tos(), conn); - file_attributes &= ~FILE_FLAG_POSIX_SEMANTICS; } - { + if (create_file_flags & CFF_DOS_PATH) { char *converted_fname; SET_STAT_INVALID(sbuf); @@ -2888,8 +3279,19 @@ NTSTATUS create_file(connection_struct *conn, goto fail; } fname = converted_fname; + } else { + if (psbuf != NULL) { + sbuf = *psbuf; + } else { + if (SMB_VFS_STAT(conn, fname, &sbuf) == -1) { + SET_STAT_INVALID(sbuf); + } + } + } + TALLOC_FREE(case_state); + /* All file access must go through check_name() */ status = check_name(conn, fname); @@ -2917,16 +3319,14 @@ NTSTATUS create_file(connection_struct *conn, if (psbuf != NULL) { *psbuf = sbuf; } - TALLOC_FREE(frame); return NT_STATUS_OK; fail: DEBUG(10, ("create_file: %s\n", nt_errstr(status))); if (fsp != NULL) { - close_file(fsp, ERROR_CLOSE); + close_file(req, fsp, ERROR_CLOSE); fsp = NULL; } - TALLOC_FREE(frame); return status; }