X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source%2Fsmbd%2Fopen.c;h=2184e69d3745c936c4b683bc9116ccf41493d9b6;hb=7f749a10eff0bb469e6eb50b02cb60fd65c23f9e;hp=b156dbbce1aaa3ba02cb5f14cf70a410627a20bd;hpb=0c21c5fa84e18d78801ab79abd726e5588902476;p=samba.git diff --git a/source/smbd/open.c b/source/smbd/open.c index b156dbbce1a..2184e69d374 100644 --- a/source/smbd/open.c +++ b/source/smbd/open.c @@ -70,15 +70,23 @@ static NTSTATUS fd_open(struct connection_struct *conn, Close the file associated with a fsp. ****************************************************************************/ -NTSTATUS fd_close(struct connection_struct *conn, files_struct *fsp) +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(conn, fsp); + + ret = SMB_VFS_CLOSE(fsp); + fsp->fh->fd = -1; + if (ret == -1) { + return map_nt_error_from_unix(errno); + } + return NT_STATUS_OK; } /**************************************************************************** @@ -102,7 +110,7 @@ static void change_file_owner_to_parent(connection_struct *conn, } become_root(); - ret = SMB_VFS_FCHOWN(fsp, fsp->fh->fd, parent_st.st_uid, (gid_t)-1); + ret = SMB_VFS_FCHOWN(fsp, parent_st.st_uid, (gid_t)-1); unbecome_root(); if (ret == -1) { DEBUG(0,("change_file_owner_to_parent: failed to fchown " @@ -125,7 +133,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 +143,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 +159,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 +208,6 @@ static NTSTATUS change_dir_owner_to_parent(connection_struct *conn, out: - TALLOC_FREE(ctx); vfs_ChDir(conn,saved_dir); return status; } @@ -342,7 +347,7 @@ static NTSTATUS open_file(files_struct *fsp, if (fsp->fh->fd == -1) { ret = SMB_VFS_STAT(conn, path, psbuf); } else { - ret = SMB_VFS_FSTAT(fsp,fsp->fh->fd,psbuf); + ret = SMB_VFS_FSTAT(fsp, psbuf); /* If we have an fd, this stat should succeed. */ if (ret == -1) { DEBUG(0,("Error doing fstat on open file %s " @@ -353,7 +358,7 @@ static NTSTATUS open_file(files_struct *fsp, /* For a non-io open, this stat failing means file not found. JRA */ if (ret == -1) { status = map_nt_error_from_unix(errno); - fd_close(conn, fsp); + fd_close(fsp); return status; } } @@ -365,7 +370,7 @@ static NTSTATUS open_file(files_struct *fsp, */ if(S_ISDIR(psbuf->st_mode)) { - fd_close(conn, fsp); + fd_close(fsp); errno = EISDIR; return NT_STATUS_FILE_IS_A_DIRECTORY; } @@ -601,12 +606,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 +613,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. */ @@ -1224,7 +1229,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 { @@ -1369,7 +1375,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, 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 +1387,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. */ @@ -1405,7 +1412,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, } #endif /* O_SYNC */ - if (posix_open & (access_mask & FILE_APPEND_DATA)) { + if (posix_open && (access_mask & FILE_APPEND_DATA)) { flags2 |= O_APPEND; } @@ -1452,11 +1459,12 @@ 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); @@ -1663,7 +1671,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, } 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,14 +1689,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(conn, fsp); + fd_close(fsp); file_free(fsp); return NT_STATUS_SHARING_VIOLATION; } @@ -1699,7 +1707,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, oplock_request)) { schedule_defer_open(lck, request_time, req); TALLOC_FREE(lck); - fd_close(conn, fsp); + fd_close(fsp); file_free(fsp); return NT_STATUS_SHARING_VIOLATION; } @@ -1718,7 +1726,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, oplock_request)) { schedule_defer_open(lck, request_time, req); TALLOC_FREE(lck); - fd_close(conn, fsp); + fd_close(fsp); file_free(fsp); return NT_STATUS_SHARING_VIOLATION; } @@ -1727,7 +1735,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, if (!NT_STATUS_IS_OK(status)) { struct deferred_open_record state; - fd_close(conn, fsp); + fd_close(fsp); file_free(fsp); state.delayed_for_oplocks = False; @@ -1764,14 +1772,16 @@ 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(conn, fsp); - file_free(fsp); - - return NT_STATUS_SHARING_VIOLATION; + TALLOC_FREE(lck); + fd_close(fsp); + file_free(fsp); + + return NT_STATUS_SHARING_VIOLATION; + } } /* @@ -1789,11 +1799,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) || - (SMB_VFS_FSTAT(fsp,fsp->fh->fd,psbuf)==-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(conn,fsp); + fd_close(fsp); file_free(fsp); return status; } @@ -1843,14 +1853,16 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, set_share_mode(lck, fsp, current_user.ut.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) + && (is_ntfs_stream_name(fname) + || can_set_initial_delete_on_close(lck))) { status = can_set_delete_on_close(fsp, True, new_dos_attributes); if (!NT_STATUS_IS_OK(status)) { /* Remember to delete the mode we just added. */ del_share_mode(lck, fsp); TALLOC_FREE(lck); - fd_close(conn,fsp); + fd_close(fsp); file_free(fsp); return status; } @@ -1864,10 +1876,15 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, if (lp_map_archive(SNUM(conn)) || lp_store_dos_attributes(SNUM(conn))) { if (!posix_open) { - file_set_dosmode(conn, fname, - new_dos_attributes | aARCH, NULL, - parent_dir, - true); + SMB_STRUCT_STAT tmp_sbuf; + SET_STAT_INVALID(tmp_sbuf); + if (file_set_dosmode( + conn, fname, + new_dos_attributes | aARCH, + &tmp_sbuf, parent_dir, + true) == 0) { + unx_mode = tmp_sbuf.st_mode; + } } } } @@ -1882,7 +1899,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 */ } @@ -1896,8 +1913,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 */ @@ -1910,7 +1926,7 @@ NTSTATUS open_file_ntcreate(connection_struct *conn, } if ((ret == -1) && - (SMB_VFS_FCHMOD(fsp, fsp->fh->fd, new_unx_mode) == -1)) + (SMB_VFS_FCHMOD(fsp, new_unx_mode) == -1)) DEBUG(5, ("open_file_ntcreate: failed to reset " "attributes of file %s to 0%o\n", fname, (unsigned int)new_unx_mode)); @@ -1974,7 +1990,7 @@ NTSTATUS open_file_fchmod(connection_struct *conn, const char *fname, NTSTATUS close_file_fchmod(files_struct *fsp) { - NTSTATUS status = fd_close(fsp->conn, fsp); + NTSTATUS status = fd_close(fsp); file_free(fsp); return status; } @@ -2089,6 +2105,7 @@ NTSTATUS open_directory(connection_struct *conn, 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, " @@ -2101,8 +2118,8 @@ NTSTATUS open_directory(connection_struct *conn, (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 )); + if (!(file_attributes & FILE_FLAG_POSIX_SEMANTICS) && is_ntfs_stream_name(fname)) { + DEBUG(2, ("open_directory: %s is a stream name!\n", fname)); return NT_STATUS_NOT_A_DIRECTORY; } @@ -2211,9 +2228,11 @@ NTSTATUS open_directory(connection_struct *conn, 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)); @@ -2262,7 +2281,7 @@ NTSTATUS open_directory(connection_struct *conn, return NT_STATUS_OK; } -NTSTATUS create_directory(connection_struct *conn, const char *directory) +NTSTATUS create_directory(connection_struct *conn, struct smb_request *req, const char *directory) { NTSTATUS status; SMB_STRUCT_STAT sbuf; @@ -2270,7 +2289,7 @@ NTSTATUS create_directory(connection_struct *conn, const char *directory) SET_STAT_INVALID(sbuf); - status = open_directory(conn, NULL, directory, &sbuf, + status = open_directory(conn, req, directory, &sbuf, FILE_READ_ATTRIBUTES, /* Just a stat open */ FILE_SHARE_NONE, /* Ignored for stat opens */ FILE_CREATE, @@ -2441,223 +2460,185 @@ static struct case_semantics_state *set_posix_case_semantics(TALLOC_CTX *mem_ctx } /* - * Wrapper around open_file_ntcreate and open_directory + * 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. */ -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) +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(); - struct case_semantics_state *case_state = NULL; - SMB_STRUCT_STAT sbuf; - int info = FILE_WAS_OPENED; - files_struct *fsp = NULL; NTSTATUS status; - DEBUG(10,("create_file: access_mask = 0x%x " - "file_attributes = 0x%x, share_access = 0x%x, " - "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", - (unsigned int)access_mask, - (unsigned int)file_attributes, - (unsigned int)share_access, - (unsigned int)create_disposition, - (unsigned int)create_options, - (unsigned int)oplock_request, - (unsigned int)root_dir_fid, - ea_list, sd, fname)); + status = SMB_VFS_STREAMINFO(conn, NULL, fname, talloc_tos(), + &num_streams, &stream_info); - SET_STAT_INVALID(sbuf); + 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 (create_options & FILE_OPEN_BY_FILE_ID) { - status = NT_STATUS_NOT_SUPPORTED; + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("SMB_VFS_STREAMINFO failed: %s\n", + nt_errstr(status))); goto fail; } - /* - * Get the file name. - */ - - 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); + DEBUG(10, ("open_streams_for_delete found %d streams\n", + num_streams)); - if (dir_fsp == NULL) { - status = NT_STATUS_INVALID_HANDLE; - goto fail; - } + if (num_streams == 0) { + TALLOC_FREE(frame); + return NT_STATUS_OK; + } - if (!dir_fsp->is_directory) { + streams = TALLOC_ARRAY(talloc_tos(), files_struct *, num_streams); + if (streams == NULL) { + DEBUG(0, ("talloc failed\n")); + status = NT_STATUS_NO_MEMORY; + goto fail; + } - /* - * Check to see if this is a mac fork of some kind. - */ + for (i=0; ifsp_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. - */ + status = create_file_unixpath + (conn, /* conn */ + NULL, /* req */ + streamname, /* fname */ + DELETE_ACCESS, /* access_mask */ + FILE_SHARE_READ | FILE_SHARE_WRITE + | FILE_SHARE_DELETE, /* share_access */ + FILE_OPEN, /* create_disposition*/ + NTCREATEX_OPTIONS_PRIVATE_STREAM_DELETE, /* create_options */ + FILE_ATTRIBUTE_NORMAL, /* file_attributes */ + 0, /* oplock_request */ + 0, /* allocation_size */ + NULL, /* sd */ + NULL, /* ea_list */ + &streams[i], /* result */ + NULL, /* pinfo */ + NULL); /* psbuf */ + + TALLOC_FREE(streamname); - 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); + if (!NT_STATUS_IS_OK(status)) { + DEBUG(10, ("Could not open stream %s: %s\n", + streamname, nt_errstr(status))); + break; + } + } - /* - * Ensure it ends in a '/'. - * We used TALLOC_SIZE +2 to add space for the '/'. - */ + /* + * don't touch the variable "status" beyond this point :-) + */ - 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'; - } + for (i -= 1 ; i >= 0; i--) { + if (streams[i] == NULL) { + continue; } - 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. - */ + DEBUG(10, ("Closing stream # %d, %s\n", i, + streams[i]->fsp_name)); + close_file(streams[i], NORMAL_CLOSE); + } - if (is_ntfs_stream_name(fname)) { - enum FAKE_FILE_TYPE fake_file_type; + fail: + TALLOC_FREE(frame); + return status; +} - fake_file_type = is_fake_file(fname); +/* + * Wrapper around open_file_ntcreate and open_directory + */ - if (fake_file_type == FAKE_FILE_TYPE_NONE) { - return NT_STATUS_OBJECT_PATH_NOT_FOUND; - } +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) +{ + SMB_STRUCT_STAT sbuf; + int info = FILE_WAS_OPENED; + files_struct *base_fsp = NULL; + files_struct *fsp = NULL; + NTSTATUS status; - /* - * Here we go! support for changing the disk quotas - * --metze - * - * We need to fake up to open this MAGIC QUOTA file - * and return a valid FID. - * - * w2k close this file directly after openening xp - * also tries a QUERY_FILE_INFO on the file and then - * close it - */ - status = open_fake_file(conn, fake_file_type, fname, - access_mask, &fsp); - if (!NT_STATUS_IS_OK(status)) { - goto fail; - } + DEBUG(10,("create_file_unixpath: access_mask = 0x%x " + "file_attributes = 0x%x, share_access = 0x%x, " + "create_disposition = 0x%x create_options = 0x%x " + "oplock_request = 0x%x ea_list = 0x%p, sd = 0x%p, " + "fname = %s\n", + (unsigned int)access_mask, + (unsigned int)file_attributes, + (unsigned int)share_access, + (unsigned int)create_disposition, + (unsigned int)create_options, + (unsigned int)oplock_request, + ea_list, sd, fname)); - goto done; - } + if (create_options & FILE_OPEN_BY_FILE_ID) { + status = NT_STATUS_NOT_SUPPORTED; + goto fail; } if (req == NULL) { oplock_request |= INTERNAL_OPEN_ONLY; } - if ((req != NULL) && (req->flags2 & FLAGS2_DFS_PATHNAMES)) { - char *resolved_fname; - - status = resolve_dfspath(talloc_tos(), conn, true, fname, - &resolved_fname); - - if (!NT_STATUS_IS_OK(status)) { - /* - * For PATH_NOT_COVERED we had - * reply_botherror(req, NT_STATUS_PATH_NOT_COVERED, - * ERRSRV, ERRbadpath); - * Need to fix in callers - */ - goto fail; - } - fname = resolved_fname; + if (psbuf != NULL) { + sbuf = *psbuf; } - - /* - * Check if POSIX semantics are wanted. - */ - - if (file_attributes & FILE_FLAG_POSIX_SEMANTICS) { - case_state = set_posix_case_semantics(talloc_tos(), conn); - file_attributes &= ~FILE_FLAG_POSIX_SEMANTICS; + else { + if (SMB_VFS_STAT(conn, fname, &sbuf) == -1) { + SET_STAT_INVALID(sbuf); + } } - { - char *converted_fname; + 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); - status = unix_convert(talloc_tos(), conn, fname, False, - &converted_fname, NULL, &sbuf); if (!NT_STATUS_IS_OK(status)) { goto fail; } - fname = converted_fname; - } - - /* All file access must go through check_name() */ - - status = check_name(conn, fname); - if (!NT_STATUS_IS_OK(status)) { - goto fail; } /* This is the correct thing to do (check every time) but can_delete @@ -2690,11 +2671,61 @@ NTSTATUS create_file(connection_struct *conn, } #endif - /* - * If it's a request for a directory open, deal with it separately. - */ + 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) { + 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; + } + } + + /* + * 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) { @@ -2745,8 +2776,6 @@ NTSTATUS create_file(connection_struct *conn, } } - TALLOC_FREE(case_state); - if (!NT_STATUS_IS_OK(status)) { goto fail; } @@ -2768,23 +2797,22 @@ NTSTATUS create_file(connection_struct *conn, uint32_t sec_info_sent = ALL_SECURITY_INFORMATION; uint32_t saved_access_mask = fsp->access_mask; - if (sd->owner_sid==0) { + if (sd->owner_sid == NULL) { sec_info_sent &= ~OWNER_SECURITY_INFORMATION; } - if (sd->group_sid==0) { + if (sd->group_sid == NULL) { sec_info_sent &= ~GROUP_SECURITY_INFORMATION; } - if (sd->sacl==0) { + if (sd->sacl == NULL) { sec_info_sent &= ~SACL_SECURITY_INFORMATION; } - if (sd->dacl==0) { + if (sd->dacl == NULL) { sec_info_sent &= ~DACL_SECURITY_INFORMATION; } fsp->access_mask = FILE_GENERIC_ALL; - status = SMB_VFS_FSET_NT_ACL( - fsp, fsp->fh->fd, sec_info_sent, sd); + status = SMB_VFS_FSET_NT_ACL(fsp, sec_info_sent, sd); fsp->access_mask = saved_access_mask; @@ -2827,6 +2855,269 @@ NTSTATUS create_file(connection_struct *conn, } } + DEBUG(10, ("create_file_unixpath: info=%d\n", info)); + + /* + * Set fsp->base_fsp late enough that we can't "goto fail" anymore. In + * the fail: branch we call close_file(fsp, ERROR_CLOSE) which would + * also close fsp->base_fsp which we have to also do explicitly in + * this routine here, as not in all "goto fail:" we have the fsp set + * up already to be initialized with the base_fsp. + */ + + fsp->base_fsp = base_fsp; + + *result = fsp; + if (pinfo != NULL) { + *pinfo = info; + } + if (psbuf != NULL) { + if ((fsp->fh == NULL) || (fsp->fh->fd == -1)) { + *psbuf = sbuf; + } + else { + SMB_VFS_FSTAT(fsp, psbuf); + } + } + return NT_STATUS_OK; + + fail: + DEBUG(10, ("create_file_unixpath: %s\n", nt_errstr(status))); + + if (fsp != NULL) { + close_file(fsp, ERROR_CLOSE); + fsp = NULL; + } + if (base_fsp != NULL) { + close_file(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) +{ + struct case_semantics_state *case_state = NULL; + SMB_STRUCT_STAT sbuf; + int info = FILE_WAS_OPENED; + files_struct *fsp = NULL; + NTSTATUS status; + + DEBUG(10,("create_file: access_mask = 0x%x " + "file_attributes = 0x%x, share_access = 0x%x, " + "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", + (unsigned int)access_mask, + (unsigned int)file_attributes, + (unsigned int)share_access, + (unsigned int)create_disposition, + (unsigned int)create_options, + (unsigned int)oplock_request, + (unsigned int)root_dir_fid, + ea_list, sd, fname)); + + /* + * Get the file name. + */ + + 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) { + + /* + * 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; + 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 = talloc_asprintf(talloc_tos(), "%s%s", parent_fname, + fname); + if (fname == NULL) { + status = NT_STATUS_NO_MEMORY; + goto fail; + } + } + + /* + * 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; + + fake_file_type = is_fake_file(fname); + + if (fake_file_type != FAKE_FILE_TYPE_NONE) { + + /* + * Here we go! support for changing the disk quotas + * --metze + * + * We need to fake up to open this MAGIC QUOTA file + * and return a valid FID. + * + * w2k close this file directly after openening xp + * also tries a QUERY_FILE_INFO on the file and then + * close it + */ + status = open_fake_file(conn, 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)) { + char *resolved_fname; + + status = resolve_dfspath(talloc_tos(), conn, true, fname, + &resolved_fname); + + if (!NT_STATUS_IS_OK(status)) { + /* + * For PATH_NOT_COVERED we had + * reply_botherror(req, NT_STATUS_PATH_NOT_COVERED, + * ERRSRV, ERRbadpath); + * Need to fix in callers + */ + goto fail; + } + fname = resolved_fname; + } + + /* + * Check if POSIX semantics are wanted. + */ + + if (file_attributes & FILE_FLAG_POSIX_SEMANTICS) { + case_state = set_posix_case_semantics(talloc_tos(), conn); + file_attributes &= ~FILE_FLAG_POSIX_SEMANTICS; + } + + { + char *converted_fname; + + SET_STAT_INVALID(sbuf); + + status = unix_convert(talloc_tos(), conn, fname, False, + &converted_fname, NULL, &sbuf); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + fname = converted_fname; + } + + TALLOC_FREE(case_state); + + /* All file access must go through check_name() */ + + status = check_name(conn, fname); + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + + status = create_file_unixpath( + conn, req, fname, access_mask, share_access, + create_disposition, create_options, file_attributes, + oplock_request, allocation_size, sd, ea_list, + &fsp, &info, &sbuf); + + if (!NT_STATUS_IS_OK(status)) { + goto fail; + } + done: DEBUG(10, ("create_file: info=%d\n", info)); @@ -2837,7 +3128,6 @@ NTSTATUS create_file(connection_struct *conn, if (psbuf != NULL) { *psbuf = sbuf; } - TALLOC_FREE(frame); return NT_STATUS_OK; fail: @@ -2847,6 +3137,5 @@ NTSTATUS create_file(connection_struct *conn, close_file(fsp, ERROR_CLOSE); fsp = NULL; } - TALLOC_FREE(frame); return status; }