static NTSTATUS update_write_time_on_close(struct files_struct *fsp) { struct smb_file_time ft; NTSTATUS status; struct share_mode_lock *lck = NULL; ZERO_STRUCT(ft); if (!fsp->update_write_time_on_close) { return NT_STATUS_OK; } if (null_timespec(fsp->close_write_time)) { fsp->close_write_time = timespec_current(); } /* Ensure we have a valid stat struct for the source. */ status = vfs_stat_fsp(fsp); if (!NT_STATUS_IS_OK(status)) { return status; } if (!VALID_STAT(fsp->fsp_name->st)) { /* if it doesn't seem to be a real file */ return NT_STATUS_OK; } /* On close if we're changing the real file time we * must update it in the open file db too. */ (void)set_write_time(fsp->file_id, fsp->close_write_time); lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL, NULL); if (lck) { /* Close write times overwrite sticky write times so we must replace any sticky write time here. */ if (!null_timespec(lck->changed_write_time)) { (void)set_sticky_write_time(fsp->file_id, fsp->close_write_time); } TALLOC_FREE(lck); } ft.mtime = fsp->close_write_time; /* We must use NULL for the fsp handle here, as smb_set_file_time() checks the fsp access_mask, which may not include FILE_WRITE_ATTRIBUTES. As this is a close based update, we are not directly changing the file attributes from a client call, but indirectly from a write. */ status = smb_set_file_time(fsp->conn, NULL, fsp->fsp_name, &ft, false); if (!NT_STATUS_IS_OK(status)) { DEBUG(10,("update_write_time_on_close: smb_set_file_time " "on file %s returned %s\n", fsp_str_dbg(fsp), nt_errstr(status))); return status; } return status; }
static NTSTATUS vfs_worm_create_file(vfs_handle_struct *handle, struct smb_request *req, uint16_t root_dir_fid, struct smb_filename *smb_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, uint32_t private_flags, struct security_descriptor *sd, struct ea_list *ea_list, files_struct **result, int *pinfo) { bool readonly = false; const uint32_t write_access_flags = FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | DELETE_ACCESS | WRITE_DAC_ACCESS | WRITE_OWNER_ACCESS; NTSTATUS status; if (VALID_STAT(smb_fname->st)) { double age; age = timespec_elapsed(&smb_fname->st.st_ex_ctime); if (age > lp_parm_int(SNUM(handle->conn), "worm", "grace_period", 3600)) { readonly = true; } } if (readonly && (access_mask & write_access_flags)) { return NT_STATUS_ACCESS_DENIED; } status = SMB_VFS_NEXT_CREATE_FILE( handle, req, root_dir_fid, smb_fname, access_mask, share_access, create_disposition, create_options, file_attributes, oplock_request, allocation_size, private_flags, sd, ea_list, result, pinfo); if (!NT_STATUS_IS_OK(status)) { return status; } /* * Access via MAXIMUM_ALLOWED_ACCESS? */ if (readonly && ((*result)->access_mask & write_access_flags)) { close_file(req, *result, NORMAL_CLOSE); return NT_STATUS_ACCESS_DENIED; } return NT_STATUS_OK; }
uint32 dos_mode(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf) { uint32 result = 0; DEBUG(8,("dos_mode: %s\n", path)); if (!VALID_STAT(*sbuf)) { return 0; } /* First do any modifications that depend on the path name. */ /* hide files with a name starting with a . */ if (lp_hide_dot_files(SNUM(conn))) { const char *p = strrchr_m(path,'/'); if (p) { p++; } else { p = path; } if (p[0] == '.' && p[1] != '.' && p[1] != 0) { result |= aHIDDEN; } } /* Get the DOS attributes from an EA by preference. */ if (get_ea_dos_attribute(conn, path, sbuf, &result)) { result |= set_sparse_flag(sbuf); } else { result |= dos_mode_from_sbuf(conn, path, sbuf); } if (S_ISREG(sbuf->st_mode)) { result |= set_offline_flag(conn, path); } /* Optimization : Only call is_hidden_path if it's not already hidden. */ if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) { result |= aHIDDEN; } DEBUG(8,("dos_mode returning ")); if (result & aHIDDEN) DEBUG(8, ("h")); if (result & aRONLY ) DEBUG(8, ("r")); if (result & aSYSTEM) DEBUG(8, ("s")); if (result & aDIR ) DEBUG(8, ("d")); if (result & aARCH ) DEBUG(8, ("a")); if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]")); DEBUG(8,("\n")); return(result); }
static NTSTATUS get_file_handle_for_metadata(connection_struct *conn, struct smb_filename *smb_fname, files_struct **ret_fsp, bool *need_close) { NTSTATUS status; files_struct *fsp; struct file_id file_id; *need_close = false; if (!VALID_STAT(smb_fname->st)) { return NT_STATUS_INVALID_PARAMETER; } file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st); for(fsp = file_find_di_first(conn->sconn, file_id); fsp; fsp = file_find_di_next(fsp)) { if (fsp->fh->fd != -1) { *ret_fsp = fsp; return NT_STATUS_OK; } } /* Opens an INTERNAL_OPEN_ONLY write handle. */ status = SMB_VFS_CREATE_FILE( conn, /* conn */ NULL, /* req */ 0, /* root_dir_fid */ smb_fname, /* fname */ 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 */ INTERNAL_OPEN_ONLY, /* oplock_request */ NULL, /* lease */ 0, /* allocation_size */ 0, /* private_flags */ NULL, /* sd */ NULL, /* ea_list */ ret_fsp, /* result */ NULL, /* pinfo */ NULL, NULL); /* create context */ if (NT_STATUS_IS_OK(status)) { *need_close = true; } return status; }
uint32_t dos_mode_msdfs(connection_struct *conn, const struct smb_filename *smb_fname) { uint32_t result = 0; DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname))); if (!VALID_STAT(smb_fname->st)) { return 0; } /* First do any modifications that depend on the path name. */ /* hide files with a name starting with a . */ if (lp_hide_dot_files(SNUM(conn))) { const char *p = strrchr_m(smb_fname->base_name, '/'); if (p) { p++; } else { p = smb_fname->base_name; } /* Only . and .. are not hidden. */ if (p[0] == '.' && !((p[1] == '\0') || (p[1] == '.' && p[2] == '\0'))) { result |= FILE_ATTRIBUTE_HIDDEN; } } result |= dos_mode_from_sbuf(conn, smb_fname); /* Optimization : Only call is_hidden_path if it's not already hidden. */ if (!(result & FILE_ATTRIBUTE_HIDDEN) && IS_HIDDEN_PATH(conn, smb_fname->base_name)) { result |= FILE_ATTRIBUTE_HIDDEN; } if (result == 0) { result = FILE_ATTRIBUTE_NORMAL; } result = filter_mode_by_protocol(result); /* * Add in that it is a reparse point */ result |= FILE_ATTRIBUTE_REPARSE_POINT; dos_mode_debug_print(__func__, result); return(result); }
static NTSTATUS update_write_time_on_close(struct files_struct *fsp) { struct smb_file_time ft; NTSTATUS status; struct share_mode_lock *lck = NULL; ZERO_STRUCT(ft); if (!fsp->update_write_time_on_close) { return NT_STATUS_OK; } if (null_timespec(fsp->close_write_time)) { fsp->close_write_time = timespec_current(); } /* Ensure we have a valid stat struct for the source. */ status = vfs_stat_fsp(fsp); if (!NT_STATUS_IS_OK(status)) { return status; } if (!VALID_STAT(fsp->fsp_name->st)) { /* if it doesn't seem to be a real file */ return NT_STATUS_OK; } /* On close if we're changing the real file time we * must update it in the open file db too. */ (void)set_write_time(fsp->file_id, fsp->close_write_time); lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL, NULL); if (lck) { /* Close write times overwrite sticky write times so we must replace any sticky write time here. */ if (!null_timespec(lck->changed_write_time)) { (void)set_sticky_write_time(fsp->file_id, fsp->close_write_time); } TALLOC_FREE(lck); } ft.mtime = fsp->close_write_time; status = smb_set_file_time(fsp->conn, fsp, fsp->fsp_name, &ft, false); if (!NT_STATUS_IS_OK(status)) { return status; } return status; }
bool can_access_file_data(connection_struct *conn, const char *fname, SMB_STRUCT_STAT *psbuf, uint32 access_mask) { if (!(access_mask & (FILE_READ_DATA|FILE_WRITE_DATA))) { return False; } access_mask &= (FILE_READ_DATA|FILE_WRITE_DATA); /* some fast paths first */ DEBUG(10,("can_access_file_data: requesting 0x%x on file %s\n", (unsigned int)access_mask, fname )); if (conn->server_info->utok.uid == 0 || conn->admin_user) { /* I'm sorry sir, I didn't know you were root... */ return True; } if (!VALID_STAT(*psbuf)) { /* Get the file permission mask and owners. */ if(SMB_VFS_STAT(conn, fname, psbuf) != 0) { return False; } } /* Check primary owner access. */ if (conn->server_info->utok.uid == psbuf->st_uid) { switch (access_mask) { case FILE_READ_DATA: return (psbuf->st_mode & S_IRUSR) ? True : False; case FILE_WRITE_DATA: return (psbuf->st_mode & S_IWUSR) ? True : False; default: /* FILE_READ_DATA|FILE_WRITE_DATA */ if ((psbuf->st_mode & (S_IWUSR|S_IRUSR)) == (S_IWUSR|S_IRUSR)) { return True; } else { return False; } } } /* now for ACL checks */ return can_access_file_acl(conn, fname, access_mask); }
static NTSTATUS update_write_time_on_close(struct files_struct *fsp) { SMB_STRUCT_STAT sbuf; struct timespec ts[2]; NTSTATUS status; int ret = -1; ZERO_STRUCT(sbuf); ZERO_STRUCT(ts); if (!fsp->update_write_time_on_close) { return NT_STATUS_OK; } if (null_timespec(fsp->close_write_time)) { fsp->close_write_time = timespec_current(); } /* Ensure we have a valid stat struct for the source. */ if (fsp->fh->fd != -1) { ret = SMB_VFS_FSTAT(fsp, &sbuf); } else { if (fsp->posix_open) { ret = SMB_VFS_LSTAT(fsp->conn,fsp->fsp_name,&sbuf); } else { ret = SMB_VFS_STAT(fsp->conn,fsp->fsp_name,&sbuf); } } if (ret == -1) { return map_nt_error_from_unix(errno); } if (!VALID_STAT(sbuf)) { /* if it doesn't seem to be a real file */ return NT_STATUS_OK; } ts[1] = fsp->close_write_time; status = smb_set_file_time(fsp->conn, fsp, fsp->fsp_name, &sbuf, ts, true); if (!NT_STATUS_IS_OK(status)) { return status; } return NT_STATUS_OK; }
/* * Check if someone changed the POSIX mode, for files we expect 0666, for * directories 0777. Discard the ACL blob if the mode is different. */ static bool nfs4acl_validate_blob(vfs_handle_struct *handle, const struct smb_filename *smb_fname) { struct nfs4acl_config *config = NULL; mode_t expected_mode; int saved_errno = 0; int ret; SMB_VFS_HANDLE_GET_DATA(handle, config, struct nfs4acl_config, return false); if (!VALID_STAT(smb_fname->st)) { /* might be a create */ return true; } if (S_ISDIR(smb_fname->st.st_ex_mode)) { expected_mode = 0777; } else { expected_mode = 0666; } if ((smb_fname->st.st_ex_mode & expected_mode) == expected_mode) { return true; } become_root(); ret = SMB_VFS_NEXT_REMOVEXATTR(handle, smb_fname, config->xattr_name); if (ret != 0) { saved_errno = errno; } unbecome_root(); if (saved_errno != 0) { errno = saved_errno; } if (ret != 0 && errno != ENOATTR) { DBG_ERR("Removing NFS4 xattr failed: %s\n", strerror(errno)); return false; } return true; }
static NTSTATUS nfs4acl_xattr_default_sd( struct vfs_handle_struct *handle, const struct smb_filename *smb_fname, TALLOC_CTX *mem_ctx, struct security_descriptor **sd) { struct nfs4acl_config *config = NULL; enum default_acl_style default_acl_style; mode_t required_mode; SMB_STRUCT_STAT sbuf = smb_fname->st; int ret; SMB_VFS_HANDLE_GET_DATA(handle, config, struct nfs4acl_config, return NT_STATUS_INTERNAL_ERROR); default_acl_style = config->default_acl_style; if (!VALID_STAT(sbuf)) { ret = vfs_stat_smb_basename(handle->conn, smb_fname, &sbuf); if (ret != 0) { return map_nt_error_from_unix(errno); } } if (S_ISDIR(sbuf.st_ex_mode)) { required_mode = 0777; } else { required_mode = 0666; } if ((sbuf.st_ex_mode & required_mode) != required_mode) { default_acl_style = DEFAULT_ACL_POSIX; } return make_default_filesystem_acl(mem_ctx, default_acl_style, smb_fname->base_name, &sbuf, sd); }
int non_posix_sys_acl_blob_get_fd_helper(vfs_handle_struct *handle, files_struct *fsp, DATA_BLOB acl_as_blob, TALLOC_CTX *mem_ctx, DATA_BLOB *blob) { SMB_STRUCT_STAT sbuf; TALLOC_CTX *frame; struct xattr_sys_acl_hash_wrapper acl_wrapper; int ret; frame = talloc_stackframe(); acl_wrapper.acl_as_blob = acl_as_blob; if (!VALID_STAT(fsp->fsp_name->st)) { ret = smb_vfs_call_fstat(handle, fsp, &sbuf); if (ret == -1) { TALLOC_FREE(frame); return -1; } } else { sbuf = fsp->fsp_name->st; } acl_wrapper.owner = sbuf.st_ex_uid; acl_wrapper.group = sbuf.st_ex_gid; acl_wrapper.mode = sbuf.st_ex_mode; if (!NDR_ERR_CODE_IS_SUCCESS(ndr_push_struct_blob(blob, mem_ctx, &acl_wrapper, (ndr_push_flags_fn_t)ndr_push_xattr_sys_acl_hash_wrapper))) { errno = EINVAL; TALLOC_FREE(frame); return -1; } TALLOC_FREE(frame); return 0; }
static bool smbacl4_fill_ace4( const struct smb_filename *filename, smbacl4_vfs_params *params, uid_t ownerUID, gid_t ownerGID, const struct security_ace *ace_nt, /* input */ SMB_ACE4PROP_T *ace_v4 /* output */ ) { DEBUG(10, ("got ace for %s\n", sid_string_dbg(&ace_nt->trustee))); memset(ace_v4, 0, sizeof(SMB_ACE4PROP_T)); /* only ACCESS|DENY supported right now */ ace_v4->aceType = ace_nt->type; ace_v4->aceFlags = map_windows_ace_flags_to_nfs4_ace_flags( ace_nt->flags); /* remove inheritance flags on files */ if (VALID_STAT(filename->st) && !S_ISDIR(filename->st.st_ex_mode)) { DEBUG(10, ("Removing inheritance flags from a file\n")); ace_v4->aceFlags &= ~(SMB_ACE4_FILE_INHERIT_ACE| SMB_ACE4_DIRECTORY_INHERIT_ACE| SMB_ACE4_NO_PROPAGATE_INHERIT_ACE| SMB_ACE4_INHERIT_ONLY_ACE); } ace_v4->aceMask = ace_nt->access_mask & (SEC_STD_ALL | SEC_FILE_ALL); se_map_generic(&ace_v4->aceMask, &file_generic_mapping); if (ace_v4->aceFlags!=ace_nt->flags) DEBUG(9, ("ace_v4->aceFlags(0x%x)!=ace_nt->flags(0x%x)\n", ace_v4->aceFlags, ace_nt->flags)); if (ace_v4->aceMask!=ace_nt->access_mask) DEBUG(9, ("ace_v4->aceMask(0x%x)!=ace_nt->access_mask(0x%x)\n", ace_v4->aceMask, ace_nt->access_mask)); if (dom_sid_equal(&ace_nt->trustee, &global_sid_World)) { ace_v4->who.special_id = SMB_ACE4_WHO_EVERYONE; ace_v4->flags |= SMB_ACE4_ID_SPECIAL; } else if (params->mode!=e_special && dom_sid_equal(&ace_nt->trustee, &global_sid_Creator_Owner)) { DEBUG(10, ("Map creator owner\n")); ace_v4->who.special_id = SMB_ACE4_WHO_OWNER; ace_v4->flags |= SMB_ACE4_ID_SPECIAL; /* A non inheriting creator owner entry has no effect. */ ace_v4->aceFlags |= SMB_ACE4_INHERIT_ONLY_ACE; if (!(ace_v4->aceFlags & SMB_ACE4_DIRECTORY_INHERIT_ACE) && !(ace_v4->aceFlags & SMB_ACE4_FILE_INHERIT_ACE)) { return false; } } else if (params->mode!=e_special && dom_sid_equal(&ace_nt->trustee, &global_sid_Creator_Group)) { DEBUG(10, ("Map creator owner group\n")); ace_v4->who.special_id = SMB_ACE4_WHO_GROUP; ace_v4->flags |= SMB_ACE4_ID_SPECIAL; /* A non inheriting creator group entry has no effect. */ ace_v4->aceFlags |= SMB_ACE4_INHERIT_ONLY_ACE; if (!(ace_v4->aceFlags & SMB_ACE4_DIRECTORY_INHERIT_ACE) && !(ace_v4->aceFlags & SMB_ACE4_FILE_INHERIT_ACE)) { return false; } } else { uid_t uid; gid_t gid; if (sid_to_gid(&ace_nt->trustee, &gid)) { ace_v4->aceFlags |= SMB_ACE4_IDENTIFIER_GROUP; ace_v4->who.gid = gid; } else if (sid_to_uid(&ace_nt->trustee, &uid)) { ace_v4->who.uid = uid; } else { DEBUG(1, ("nfs4_acls.c: file [%s]: could not " "convert %s to uid or gid\n", filename->base_name, sid_string_dbg(&ace_nt->trustee))); return false; } } return true; /* OK */ }
NTSTATUS unix_convert(TALLOC_CTX *ctx, connection_struct *conn, const char *orig_path, bool allow_wcard_last_component, char **pp_conv_path, char **pp_saved_last_component, SMB_STRUCT_STAT *pst) { SMB_STRUCT_STAT st; char *start, *end; char *dirpath = NULL; char *name = NULL; char *stream = NULL; bool component_was_mangled = False; bool name_has_wildcard = False; bool posix_pathnames = false; NTSTATUS result; int ret = -1; SET_STAT_INVALID(*pst); *pp_conv_path = NULL; if(pp_saved_last_component) { *pp_saved_last_component = NULL; } if (conn->printer) { /* we don't ever use the filenames on a printer share as a filename - so don't convert them */ if (!(*pp_conv_path = talloc_strdup(ctx,orig_path))) { return NT_STATUS_NO_MEMORY; } return NT_STATUS_OK; } DEBUG(5, ("unix_convert called on file \"%s\"\n", orig_path)); /* * Conversion to basic unix format is already done in * check_path_syntax(). */ /* * Names must be relative to the root of the service - any leading /. * and trailing /'s should have been trimmed by check_path_syntax(). */ #ifdef DEVELOPER SMB_ASSERT(*orig_path != '/'); #endif /* * If we trimmed down to a single '\0' character * then we should use the "." directory to avoid * searching the cache, but not if we are in a * printing share. * As we know this is valid we can return true here. */ if (!*orig_path) { if (!(name = talloc_strdup(ctx,"."))) { return NT_STATUS_NO_MEMORY; } if (SMB_VFS_STAT(conn,name,&st) == 0) { *pst = st; } else { return map_nt_error_from_unix(errno); } DEBUG(5,("conversion finished \"\" -> %s\n",name)); goto done; } if (orig_path[0] == '.' && (orig_path[1] == '/' || orig_path[1] == '\0')) { /* Start of pathname can't be "." only. */ if (orig_path[1] == '\0' || orig_path[2] == '\0') { result = NT_STATUS_OBJECT_NAME_INVALID; } else { result =determine_path_error( &orig_path[2], allow_wcard_last_component); } return result; } if (!(name = talloc_strdup(ctx, orig_path))) { DEBUG(0, ("talloc_strdup failed\n")); return NT_STATUS_NO_MEMORY; } /* * Large directory fix normalization. If we're case sensitive, and * the case preserving parameters are set to "no", normalize the case of * the incoming filename from the client WHETHER IT EXISTS OR NOT ! * This is in conflict with the current (3.0.20) man page, but is * what people expect from the "large directory howto". I'll update * the man page. Thanks to [email protected] for finding this. JRA. */ if (conn->case_sensitive && !conn->case_preserve && !conn->short_case_preserve) { strnorm(name, lp_defaultcase(SNUM(conn))); } /* * Ensure saved_last_component is valid even if file exists. */ if(pp_saved_last_component) { end = strrchr_m(name, '/'); if (end) { *pp_saved_last_component = talloc_strdup(ctx, end + 1); } else { *pp_saved_last_component = talloc_strdup(ctx, name); } } posix_pathnames = lp_posix_pathnames(); if (!posix_pathnames) { stream = strchr_m(name, ':'); if (stream != NULL) { char *tmp = talloc_strdup(ctx, stream); if (tmp == NULL) { TALLOC_FREE(name); return NT_STATUS_NO_MEMORY; } *stream = '\0'; stream = tmp; } } start = name; /* If we're providing case insentive semantics or * the underlying filesystem is case insensitive, * then a case-normalized hit in the stat-cache is * authoratitive. JRA. */ if((!conn->case_sensitive || !(conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)) && stat_cache_lookup(conn, &name, &dirpath, &start, &st)) { *pst = st; goto done; } /* * Make sure "dirpath" is an allocated string, we use this for * building the directories with asprintf and free it. */ if ((dirpath == NULL) && (!(dirpath = talloc_strdup(ctx,"")))) { DEBUG(0, ("talloc_strdup failed\n")); TALLOC_FREE(name); return NT_STATUS_NO_MEMORY; } /* * stat the name - if it exists then we are all done! */ if (posix_pathnames) { ret = SMB_VFS_LSTAT(conn,name,&st); } else { ret = SMB_VFS_STAT(conn,name,&st); } if (ret == 0) { /* Ensure we catch all names with in "/." this is disallowed under Windows. */ const char *p = strstr(name, "/."); /* mb safe. */ if (p) { if (p[2] == '/') { /* Error code within a pathname. */ result = NT_STATUS_OBJECT_PATH_NOT_FOUND; goto fail; } else if (p[2] == '\0') { /* Error code at the end of a pathname. */ result = NT_STATUS_OBJECT_NAME_INVALID; goto fail; } } stat_cache_add(orig_path, name, conn->case_sensitive); DEBUG(5,("conversion finished %s -> %s\n",orig_path, name)); *pst = st; goto done; } DEBUG(5,("unix_convert begin: name = %s, dirpath = %s, start = %s\n", name, dirpath, start)); /* * A special case - if we don't have any mangling chars and are case * sensitive or the underlying filesystem is case insentive then searching * won't help. */ if ((conn->case_sensitive || !(conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)) && !mangle_is_mangled(name, conn->params)) { goto done; } /* * is_mangled() was changed to look at an entire pathname, not * just a component. JRA. */ if (mangle_is_mangled(start, conn->params)) { component_was_mangled = True; } /* * Now we need to recursively match the name against the real * directory structure. */ /* * Match each part of the path name separately, trying the names * as is first, then trying to scan the directory for matching names. */ for (; start ; start = (end?end+1:(char *)NULL)) { /* * Pinpoint the end of this section of the filename. */ /* mb safe. '/' can't be in any encoded char. */ end = strchr(start, '/'); /* * Chop the name at this point. */ if (end) { *end = 0; } if (pp_saved_last_component) { TALLOC_FREE(*pp_saved_last_component); *pp_saved_last_component = talloc_strdup(ctx, end ? end + 1 : start); if (!*pp_saved_last_component) { DEBUG(0, ("talloc failed\n")); return NT_STATUS_NO_MEMORY; } } /* The name cannot have a component of "." */ if (ISDOT(start)) { if (!end) { /* Error code at the end of a pathname. */ result = NT_STATUS_OBJECT_NAME_INVALID; } else { result = determine_path_error(end+1, allow_wcard_last_component); } goto fail; } /* The name cannot have a wildcard if it's not the last component. */ name_has_wildcard = ms_has_wild(start); /* Wildcard not valid anywhere. */ if (name_has_wildcard && !allow_wcard_last_component) { result = NT_STATUS_OBJECT_NAME_INVALID; goto fail; } /* Wildcards never valid within a pathname. */ if (name_has_wildcard && end) { result = NT_STATUS_OBJECT_NAME_INVALID; goto fail; } /* * Check if the name exists up to this point. */ if (posix_pathnames) { ret = SMB_VFS_LSTAT(conn,name, &st); } else { ret = SMB_VFS_STAT(conn,name, &st); } if (ret == 0) { /* * It exists. it must either be a directory or this must * be the last part of the path for it to be OK. */ if (end && !(st.st_mode & S_IFDIR)) { /* * An intermediate part of the name isn't * a directory. */ DEBUG(5,("Not a dir %s\n",start)); *end = '/'; /* * We need to return the fact that the * intermediate name resolution failed. This * is used to return an error of ERRbadpath * rather than ERRbadfile. Some Windows * applications depend on the difference between * these two errors. */ result = NT_STATUS_OBJECT_PATH_NOT_FOUND; goto fail; } if (!end) { /* * We just scanned for, and found the end of * the path. We must return the valid stat * struct. JRA. */ *pst = st; } } else { char *found_name = NULL; /* Stat failed - ensure we don't use it. */ SET_STAT_INVALID(st); /* * Reset errno so we can detect * directory open errors. */ errno = 0; /* * Try to find this part of the path in the directory. */ if (name_has_wildcard || (get_real_filename_mangled( conn, dirpath, start, talloc_tos(), &found_name) == -1)) { char *unmangled; if (end) { /* * An intermediate part of the name * can't be found. */ DEBUG(5,("Intermediate not found %s\n", start)); *end = '/'; /* * We need to return the fact that the * intermediate name resolution failed. * This is used to return an error of * ERRbadpath rather than ERRbadfile. * Some Windows applications depend on * the difference between these two * errors. */ /* * ENOENT, ENOTDIR and ELOOP all map * to NT_STATUS_OBJECT_PATH_NOT_FOUND * in the filename walk. */ if (errno == ENOENT || errno == ENOTDIR || errno == ELOOP) { result = NT_STATUS_OBJECT_PATH_NOT_FOUND; } else { result = map_nt_error_from_unix(errno); } goto fail; } /* ENOENT is the only valid error here. */ if ((errno != 0) && (errno != ENOENT)) { /* * ENOTDIR and ELOOP both map to * NT_STATUS_OBJECT_PATH_NOT_FOUND * in the filename walk. */ if (errno == ENOTDIR || errno == ELOOP) { result = NT_STATUS_OBJECT_PATH_NOT_FOUND; } else { result = map_nt_error_from_unix(errno); } goto fail; } /* * Just the last part of the name doesn't exist. * We need to strupper() or strlower() it as * this conversion may be used for file creation * purposes. Fix inspired by * Thomas Neumann <*****@*****.**>. */ if (!conn->case_preserve || (mangle_is_8_3(start, False, conn->params) && !conn->short_case_preserve)) { strnorm(start, lp_defaultcase(SNUM(conn))); } /* * check on the mangled stack to see if we can * recover the base of the filename. */ if (mangle_is_mangled(start, conn->params) && mangle_lookup_name_from_8_3(ctx, start, &unmangled, conn->params)) { char *tmp; size_t start_ofs = start - name; if (*dirpath != '\0') { tmp = talloc_asprintf(ctx, "%s/%s", dirpath, unmangled); TALLOC_FREE(unmangled); } else { tmp = unmangled; } if (tmp == NULL) { DEBUG(0, ("talloc failed\n")); return NT_STATUS_NO_MEMORY; } TALLOC_FREE(name); name = tmp; start = name + start_ofs; end = start + strlen(start); } DEBUG(5,("New file %s\n",start)); goto done; } /* * Restore the rest of the string. If the string was * mangled the size may have changed. */ if (end) { char *tmp; size_t start_ofs = start - name; if (*dirpath != '\0') { tmp = talloc_asprintf(ctx, "%s/%s/%s", dirpath, found_name, end+1); } else { tmp = talloc_asprintf(ctx, "%s/%s", found_name, end+1); } if (tmp == NULL) { DEBUG(0, ("talloc_asprintf failed\n")); return NT_STATUS_NO_MEMORY; } TALLOC_FREE(name); name = tmp; start = name + start_ofs; end = start + strlen(found_name); *end = '\0'; } else { char *tmp; size_t start_ofs = start - name; if (*dirpath != '\0') { tmp = talloc_asprintf(ctx, "%s/%s", dirpath, found_name); } else { tmp = talloc_strdup(ctx, found_name); } if (tmp == NULL) { DEBUG(0, ("talloc failed\n")); return NT_STATUS_NO_MEMORY; } TALLOC_FREE(name); name = tmp; start = name + start_ofs; /* * We just scanned for, and found the end of * the path. We must return a valid stat struct * if it exists. JRA. */ if (posix_pathnames) { ret = SMB_VFS_LSTAT(conn,name, &st); } else { ret = SMB_VFS_STAT(conn,name, &st); } if (ret == 0) { *pst = st; } else { SET_STAT_INVALID(st); } } TALLOC_FREE(found_name); } /* end else */ #ifdef DEVELOPER /* * This sucks! * We should never provide different behaviors * depending on DEVELOPER!!! */ if (VALID_STAT(st)) { bool delete_pending; get_file_infos(vfs_file_id_from_sbuf(conn, &st), &delete_pending, NULL); if (delete_pending) { result = NT_STATUS_DELETE_PENDING; goto fail; } } #endif /* * Add to the dirpath that we have resolved so far. */ if (*dirpath != '\0') { char *tmp = talloc_asprintf(ctx, "%s/%s", dirpath, start); if (!tmp) { DEBUG(0, ("talloc_asprintf failed\n")); return NT_STATUS_NO_MEMORY; } TALLOC_FREE(dirpath); dirpath = tmp; } else { TALLOC_FREE(dirpath); if (!(dirpath = talloc_strdup(ctx,start))) { DEBUG(0, ("talloc_strdup failed\n")); return NT_STATUS_NO_MEMORY; } } /* * Don't cache a name with mangled or wildcard components * as this can change the size. */ if(!component_was_mangled && !name_has_wildcard) { stat_cache_add(orig_path, dirpath, conn->case_sensitive); } /* * Restore the / that we wiped out earlier. */ if (end) { *end = '/'; } } /* * Don't cache a name with mangled or wildcard components * as this can change the size. */ if(!component_was_mangled && !name_has_wildcard) { stat_cache_add(orig_path, name, conn->case_sensitive); } /* * The name has been resolved. */ DEBUG(5,("conversion finished %s -> %s\n",orig_path, name)); done: if (stream != NULL) { char *tmp = NULL; result = build_stream_path(ctx, conn, orig_path, name, stream, pst, &tmp); if (!NT_STATUS_IS_OK(result)) { goto fail; } DEBUG(10, ("build_stream_path returned %s\n", tmp)); TALLOC_FREE(name); name = tmp; } *pp_conv_path = name; TALLOC_FREE(dirpath); return NT_STATUS_OK; fail: DEBUG(10, ("dirpath = [%s] start = [%s]\n", dirpath, start)); if (*dirpath != '\0') { *pp_conv_path = talloc_asprintf(ctx, "%s/%s", dirpath, start); } else { *pp_conv_path = talloc_strdup(ctx, start); } if (!*pp_conv_path) { DEBUG(0, ("talloc_asprintf failed\n")); return NT_STATUS_NO_MEMORY; } TALLOC_FREE(name); TALLOC_FREE(dirpath); return result; }
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; mode_t tmp; mode_t unixmode; int ret = -1; /* We only allow READONLY|HIDDEN|SYSTEM|DIRECTORY|ARCHIVE here. */ dosmode &= SAMBA_ATTRIBUTES_MASK; DEBUG(10,("file_set_dosmode: setting dos mode 0x%x on file %s\n", dosmode, fname)); if (st == NULL) { SET_STAT_INVALID(st1); st = &st1; } if (!VALID_STAT(*st)) { if (SMB_VFS_STAT(conn,fname,st)) return(-1); } unixmode = st->st_mode; get_acl_group_bits(conn, fname, &st->st_mode); if (S_ISDIR(st->st_mode)) dosmode |= aDIR; else dosmode &= ~aDIR; if (dos_mode(conn,fname,st) == dosmode) { st->st_mode = unixmode; return(0); } /* Store the DOS attributes in an EA by preference. */ if (set_ea_dos_attribute(conn, fname, st, dosmode)) { st->st_mode = unixmode; return 0; } unixmode = unix_mode(conn,dosmode,fname, parent_dir); /* preserve the s bits */ mask |= (S_ISUID | S_ISGID); /* preserve the t bit */ #ifdef S_ISVTX mask |= S_ISVTX; #endif /* possibly preserve the x bits */ if (!MAP_ARCHIVE(conn)) mask |= S_IXUSR; if (!MAP_SYSTEM(conn)) mask |= S_IXGRP; if (!MAP_HIDDEN(conn)) mask |= S_IXOTH; unixmode |= (st->st_mode & mask); /* if we previously had any r bits set then leave them alone */ if ((tmp = st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) { unixmode &= ~(S_IRUSR|S_IRGRP|S_IROTH); unixmode |= tmp; } /* if we previously had any w bits set then leave them alone whilst adding in the new w bits, if the new mode is not rdonly */ if (!IS_DOS_READONLY(dosmode)) { unixmode |= (st->st_mode & (S_IWUSR|S_IWGRP|S_IWOTH)); } if ((ret = SMB_VFS_CHMOD(conn,fname,unixmode)) == 0) { notify_fname(conn, NOTIFY_ACTION_MODIFIED, FILE_NOTIFY_CHANGE_ATTRIBUTES, fname); st->st_mode = unixmode; return 0; } if((errno != EPERM) && (errno != EACCES)) return -1; if(!lp_dos_filemode(SNUM(conn))) return -1; /* We want DOS semantics, ie allow non owner with write permission to change the bits on a file. Just like file_ntimes below. */ /* Check if we have write access. */ if (CAN_WRITE(conn)) { /* * We need to open the file with write access whilst * still in our current user context. This ensures we * are not violating security in doing the fchmod. * This file open does *not* break any oplocks we are * holding. We need to review this.... may need to * break batch oplocks open by others. JRA. */ files_struct *fsp; if (!NT_STATUS_IS_OK(open_file_fchmod(conn,fname,st,&fsp))) return -1; become_root(); ret = SMB_VFS_FCHMOD(fsp, fsp->fh->fd, unixmode); unbecome_root(); close_file_fchmod(fsp); notify_fname(conn, NOTIFY_ACTION_MODIFIED, FILE_NOTIFY_CHANGE_ATTRIBUTES, fname); if (ret == 0) { st->st_mode = unixmode; } } return( ret ); }
static NTSTATUS update_write_time_on_close(struct files_struct *fsp) { struct smb_file_time ft; NTSTATUS status; struct share_mode_lock *lck = NULL; ZERO_STRUCT(ft); if (!fsp->update_write_time_on_close) { return NT_STATUS_OK; } if (null_timespec(fsp->close_write_time)) { fsp->close_write_time = timespec_current(); } /* Ensure we have a valid stat struct for the source. */ status = vfs_stat_fsp(fsp); if (!NT_STATUS_IS_OK(status)) { return status; } if (!VALID_STAT(fsp->fsp_name->st)) { /* if it doesn't seem to be a real file */ return NT_STATUS_OK; } /* * get_existing_share_mode_lock() isn't really the right * call here, as we're being called after * close_remove_share_mode() inside close_normal_file() * so it's quite normal to not have an existing share * mode here. However, get_share_mode_lock() doesn't * work because that will create a new share mode if * one doesn't exist - so stick with this call (just * ignore any error we get if the share mode doesn't * exist. */ lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id); if (lck) { /* On close if we're changing the real file time we * must update it in the open file db too. */ (void)set_write_time(fsp->file_id, fsp->close_write_time); /* Close write times overwrite sticky write times so we must replace any sticky write time here. */ if (!null_timespec(lck->data->changed_write_time)) { (void)set_sticky_write_time(fsp->file_id, fsp->close_write_time); } TALLOC_FREE(lck); } ft.mtime = fsp->close_write_time; /* As this is a close based update, we are not directly changing the file attributes from a client call, but indirectly from a write. */ status = smb_set_file_time(fsp->conn, fsp, fsp->fsp_name, &ft, false); if (!NT_STATUS_IS_OK(status)) { DEBUG(10,("update_write_time_on_close: smb_set_file_time " "on file %s returned %s\n", fsp_str_dbg(fsp), nt_errstr(status))); return status; } return status; }
static BOOL set_ea_dos_attribute(connection_struct *conn, const char *path, SMB_STRUCT_STAT *sbuf, uint32 dosmode) { fstring attrstr; files_struct *fsp = NULL; BOOL ret = False; if (!lp_store_dos_attributes(SNUM(conn))) { return False; } snprintf(attrstr, sizeof(attrstr)-1, "0x%x", dosmode & SAMBA_ATTRIBUTES_MASK); if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == -1) { if((errno != EPERM) && (errno != EACCES)) { if (errno == ENOSYS #if defined(ENOTSUP) || errno == ENOTSUP) { #else ) { #endif set_store_dos_attributes(SNUM(conn), False); } return False; } /* We want DOS semantics, ie allow non owner with write permission to change the bits on a file. Just like file_ntimes below. */ /* Check if we have write access. */ if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn))) return False; /* * We need to open the file with write access whilst * still in our current user context. This ensures we * are not violating security in doing the setxattr. */ if (!NT_STATUS_IS_OK(open_file_fchmod(conn,path,sbuf,&fsp))) return ret; become_root(); if (SMB_VFS_SETXATTR(conn, path, SAMBA_XATTR_DOS_ATTRIB, attrstr, strlen(attrstr), 0) == 0) { ret = True; } unbecome_root(); close_file_fchmod(fsp); return ret; } DEBUG(10,("set_ea_dos_attribute: set EA %s on file %s\n", attrstr, path)); return True; } /**************************************************************************** Change a unix mode to a dos mode for an ms dfs link. ****************************************************************************/ uint32 dos_mode_msdfs(connection_struct *conn, const char *path,SMB_STRUCT_STAT *sbuf) { uint32 result = 0; DEBUG(8,("dos_mode_msdfs: %s\n", path)); if (!VALID_STAT(*sbuf)) { return 0; } /* First do any modifications that depend on the path name. */ /* hide files with a name starting with a . */ if (lp_hide_dot_files(SNUM(conn))) { const char *p = strrchr_m(path,'/'); if (p) { p++; } else { p = path; } if (p[0] == '.' && p[1] != '.' && p[1] != 0) { result |= aHIDDEN; } } result |= dos_mode_from_sbuf(conn, path, sbuf); /* Optimization : Only call is_hidden_path if it's not already hidden. */ if (!(result & aHIDDEN) && IS_HIDDEN_PATH(conn,path)) { result |= aHIDDEN; } DEBUG(8,("dos_mode_msdfs returning ")); if (result & aHIDDEN) DEBUG(8, ("h")); if (result & aRONLY ) DEBUG(8, ("r")); if (result & aSYSTEM) DEBUG(8, ("s")); if (result & aDIR ) DEBUG(8, ("d")); if (result & aARCH ) DEBUG(8, ("a")); if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]")); DEBUG(8,("\n")); return(result); }
bool can_delete_file_in_directory(connection_struct *conn, const struct smb_filename *smb_fname) { TALLOC_CTX *ctx = talloc_tos(); char *dname = NULL; struct smb_filename *smb_fname_parent = NULL; NTSTATUS status; bool ret; if (!CAN_WRITE(conn)) { return False; } if (!lp_acl_check_permissions(SNUM(conn))) { /* This option means don't check. */ return true; } /* Get the parent directory permission mask and owners. */ if (!parent_dirname(ctx, smb_fname->base_name, &dname, NULL)) { return False; } status = create_synthetic_smb_fname(ctx, dname, NULL, NULL, &smb_fname_parent); if (!NT_STATUS_IS_OK(status)) { ret = false; goto out; } if(SMB_VFS_STAT(conn, smb_fname_parent) != 0) { ret = false; goto out; } /* fast paths first */ if (!S_ISDIR(smb_fname_parent->st.st_ex_mode)) { ret = false; goto out; } if (get_current_uid(conn) == (uid_t)0) { /* I'm sorry sir, I didn't know you were root... */ ret = true; goto out; } #ifdef S_ISVTX /* sticky bit means delete only by owner of file or by root or * by owner of directory. */ if (smb_fname_parent->st.st_ex_mode & S_ISVTX) { if (!VALID_STAT(smb_fname->st)) { /* If the file doesn't already exist then * yes we'll be able to delete it. */ ret = true; goto out; } /* * Patch from SATOH Fumiyasu <*****@*****.**> * for bug #3348. Don't assume owning sticky bit * directory means write access allowed. * Fail to delete if we're not the owner of the file, * or the owner of the directory as we have no possible * chance of deleting. Otherwise, go on and check the ACL. */ if ((get_current_uid(conn) != smb_fname_parent->st.st_ex_uid) && (get_current_uid(conn) != smb_fname->st.st_ex_uid)) { DEBUG(10,("can_delete_file_in_directory: not " "owner of file %s or directory %s", smb_fname_str_dbg(smb_fname), smb_fname_str_dbg(smb_fname_parent))); ret = false; goto out; } } #endif /* now for ACL checks */ /* * There's two ways to get the permission to delete a file: First by * having the DELETE bit on the file itself and second if that does * not help, by the DELETE_CHILD bit on the containing directory. * * Here we only check the directory permissions, we will * check the file DELETE permission separately. */ ret = NT_STATUS_IS_OK(smbd_check_access_rights(conn, smb_fname_parent, FILE_DELETE_CHILD)); out: TALLOC_FREE(dname); TALLOC_FREE(smb_fname_parent); return ret; }
static NTSTATUS nfs4acl_xattr_fset_nt_acl(vfs_handle_struct *handle, files_struct *fsp, uint32_t security_info_sent, const struct security_descriptor *psd) { struct nfs4acl_config *config = NULL; const struct security_token *token = NULL; mode_t existing_mode; mode_t expected_mode; mode_t restored_mode; bool chown_needed = false; NTSTATUS status; int ret; SMB_VFS_HANDLE_GET_DATA(handle, config, struct nfs4acl_config, return NT_STATUS_INTERNAL_ERROR); if (!VALID_STAT(fsp->fsp_name->st)) { DBG_ERR("Invalid stat info on [%s]\n", fsp_str_dbg(fsp)); return NT_STATUS_INTERNAL_ERROR; } existing_mode = fsp->fsp_name->st.st_ex_mode; if (S_ISDIR(existing_mode)) { expected_mode = 0777; } else { expected_mode = 0666; } if ((existing_mode & expected_mode) != expected_mode) { int saved_errno = 0; restored_mode = existing_mode | expected_mode; become_root(); if (fsp->fh->fd != -1) { ret = SMB_VFS_NEXT_FCHMOD(handle, fsp, restored_mode); } else { ret = SMB_VFS_NEXT_CHMOD(handle, fsp->fsp_name, restored_mode); } if (ret != 0) { saved_errno = errno; } unbecome_root(); if (saved_errno != 0) { errno = saved_errno; } if (ret != 0) { DBG_ERR("Resetting POSIX mode on [%s] from [0%o]: %s\n", fsp_str_dbg(fsp), existing_mode, strerror(errno)); return map_nt_error_from_unix(errno); } } status = smb_set_nt_acl_nfs4(handle, fsp, &config->nfs4_params, security_info_sent, psd, nfs4acl_smb4acl_set_fn); if (NT_STATUS_IS_OK(status)) { return NT_STATUS_OK; } /* * We got access denied. If we're already root, or we didn't * need to do a chown, or the fsp isn't open with WRITE_OWNER * access, just return. */ if ((security_info_sent & SECINFO_OWNER) && (psd->owner_sid != NULL)) { chown_needed = true; } if ((security_info_sent & SECINFO_GROUP) && (psd->group_sid != NULL)) { chown_needed = true; } if (get_current_uid(handle->conn) == 0 || chown_needed == false || !(fsp->access_mask & SEC_STD_WRITE_OWNER)) { return NT_STATUS_ACCESS_DENIED; } /* * Only allow take-ownership, not give-ownership. That's the way Windows * implements SEC_STD_WRITE_OWNER. MS-FSA 2.1.5.16 just states: If * InputBuffer.OwnerSid is not a valid owner SID for a file in the * objectstore, as determined in an implementation specific manner, the * object store MUST return STATUS_INVALID_OWNER. */ token = get_current_nttok(fsp->conn); if (!security_token_is_sid(token, psd->owner_sid)) { return NT_STATUS_INVALID_OWNER; } DBG_DEBUG("overriding chown on file %s for sid %s\n", fsp_str_dbg(fsp), sid_string_tos(psd->owner_sid)); become_root(); status = smb_set_nt_acl_nfs4(handle, fsp, &config->nfs4_params, security_info_sent, psd, nfs4acl_smb4acl_set_fn); unbecome_root(); return status; }
static BOOL open_file(files_struct *fsp,connection_struct *conn, const char *fname1,SMB_STRUCT_STAT *psbuf,int flags,mode_t mode, uint32 desired_access) { extern struct current_user current_user; pstring fname; int accmode = (flags & O_ACCMODE); int local_flags = flags; fsp->fd = -1; fsp->oplock_type = NO_OPLOCK; errno = EPERM; pstrcpy(fname,fname1); /* Check permissions */ /* * This code was changed after seeing a client open request * containing the open mode of (DENY_WRITE/read-only) with * the 'create if not exist' bit set. The previous code * would fail to open the file read only on a read-only share * as it was checking the flags parameter directly against O_RDONLY, * this was failing as the flags parameter was set to O_RDONLY|O_CREAT. * JRA. */ if (!CAN_WRITE(conn)) { /* It's a read-only share - fail if we wanted to write. */ if(accmode != O_RDONLY) { DEBUG(3,("Permission denied opening %s\n",fname)); check_for_pipe(fname); return False; } else if(flags & O_CREAT) { /* We don't want to write - but we must make sure that O_CREAT doesn't create the file if we have write access into the directory. */ flags &= ~O_CREAT; } } /* * This little piece of insanity is inspired by the * fact that an NT client can open a file for O_RDONLY, * but set the create disposition to FILE_EXISTS_TRUNCATE. * If the client *can* write to the file, then it expects to * truncate the file, even though it is opening for readonly. * Quicken uses this stupid trick in backup file creation... * Thanks *greatly* to "David W. Chapman Jr." <*****@*****.**> * for helping track this one down. It didn't bite us in 2.0.x * as we always opened files read-write in that release. JRA. */ if ((accmode == O_RDONLY) && ((flags & O_TRUNC) == O_TRUNC)) { DEBUG(10,("open_file: truncate requested on read-only open for file %s\n",fname )); local_flags = (flags & ~O_ACCMODE)|O_RDWR; } /* actually do the open */ if ((desired_access & (FILE_READ_DATA|FILE_WRITE_DATA|FILE_APPEND_DATA|FILE_EXECUTE)) || (local_flags & O_CREAT) || ((local_flags & O_TRUNC) == O_TRUNC) ) { /* * We can't actually truncate here as the file may be locked. * open_file_shared will take care of the truncate later. JRA. */ local_flags &= ~O_TRUNC; #if defined(O_NONBLOCK) && defined(S_ISFIFO) /* * We would block on opening a FIFO with no one else on the * other end. Do what we used to do and add O_NONBLOCK to the * open flags. JRA. */ if (VALID_STAT(*psbuf) && S_ISFIFO(psbuf->st_mode)) local_flags |= O_NONBLOCK; #endif fsp->fd = fd_open(conn, fname, local_flags, mode); if (fsp->fd == -1) { DEBUG(3,("Error opening file %s (%s) (local_flags=%d) (flags=%d)\n", fname,strerror(errno),local_flags,flags)); check_for_pipe(fname); return False; } } else fsp->fd = -1; /* What we used to call a stat open. */ if (!VALID_STAT(*psbuf)) { int ret; if (fsp->fd == -1) ret = vfs_stat(conn, fname, psbuf); else { ret = vfs_fstat(fsp,fsp->fd,psbuf); /* If we have an fd, this stat should succeed. */ if (ret == -1) DEBUG(0,("Error doing fstat on open file %s (%s)\n", fname,strerror(errno) )); } /* For a non-io open, this stat failing means file not found. JRA */ if (ret == -1) { fd_close(conn, fsp); return False; } } /* * POSIX allows read-only opens of directories. We don't * want to do this (we use a different code path for this) * so catch a directory open and return an EISDIR. JRA. */ if(S_ISDIR(psbuf->st_mode)) { fd_close(conn, fsp); errno = EISDIR; return False; } fsp->mode = psbuf->st_mode; fsp->inode = psbuf->st_ino; fsp->dev = psbuf->st_dev; fsp->vuid = current_user.vuid; fsp->size = psbuf->st_size; fsp->pos = -1; fsp->can_lock = True; fsp->can_read = ((flags & O_WRONLY)==0); fsp->can_write = ((flags & (O_WRONLY|O_RDWR))!=0); fsp->share_mode = 0; fsp->desired_access = desired_access; fsp->print_file = False; fsp->modified = False; fsp->oplock_type = NO_OPLOCK; fsp->sent_oplock_break = NO_BREAK_SENT; fsp->is_directory = False; fsp->directory_delete_on_close = False; fsp->conn = conn; /* * Note that the file name here is the *untranslated* name * ie. it is still in the DOS codepage sent from the client. * All use of this filename will pass though the sys_xxxx * functions which will do the dos_to_unix translation before * mapping into a UNIX filename. JRA. */ string_set(&fsp->fsp_name,fname); fsp->wbmpx_ptr = NULL; 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, BOOLSTR(fsp->can_read), BOOLSTR(fsp->can_write), conn->num_files_open + 1)); return True; }
NTSTATUS unix_convert(connection_struct *conn, pstring name, BOOL allow_wcard_last_component, char *saved_last_component, SMB_STRUCT_STAT *pst) { SMB_STRUCT_STAT st; char *start, *end; pstring dirpath; pstring orig_path; BOOL component_was_mangled = False; BOOL name_has_wildcard = False; SET_STAT_INVALID(*pst); *dirpath = 0; if(saved_last_component) { *saved_last_component = 0; } if (conn->printer) { /* we don't ever use the filenames on a printer share as a filename - so don't convert them */ return NT_STATUS_OK; } DEBUG(5, ("unix_convert called on file \"%s\"\n", name)); /* * Conversion to basic unix format is already done in check_path_syntax(). */ /* * Names must be relative to the root of the service - any leading /. * and trailing /'s should have been trimmed by check_path_syntax(). */ #ifdef DEVELOPER SMB_ASSERT(*name != '/'); #endif /* * If we trimmed down to a single '\0' character * then we should use the "." directory to avoid * searching the cache, but not if we are in a * printing share. * As we know this is valid we can return true here. */ if (!*name) { name[0] = '.'; name[1] = '\0'; if (SMB_VFS_STAT(conn,name,&st) == 0) { *pst = st; } DEBUG(5,("conversion finished \"\" -> %s\n",name)); return NT_STATUS_OK; } if (name[0] == '.' && (name[1] == '/' || name[1] == '\0')) { /* Start of pathname can't be "." only. */ if (name[1] == '\0' || name[2] == '\0') { return NT_STATUS_OBJECT_NAME_INVALID; } else { return determine_path_error(&name[2], allow_wcard_last_component); } } /* * Ensure saved_last_component is valid even if file exists. */ if(saved_last_component) { end = strrchr_m(name, '/'); if (end) { pstrcpy(saved_last_component, end + 1); } else { pstrcpy(saved_last_component, name); } } /* * Large directory fix normalization. If we're case sensitive, and * the case preserving parameters are set to "no", normalize the case of * the incoming filename from the client WHETHER IT EXISTS OR NOT ! * This is in conflict with the current (3.0.20) man page, but is * what people expect from the "large directory howto". I'll update * the man page. Thanks to [email protected] for finding this. JRA. */ if (conn->case_sensitive && !conn->case_preserve && !conn->short_case_preserve) { strnorm(name, lp_defaultcase(SNUM(conn))); } start = name; pstrcpy(orig_path, name); if(!conn->case_sensitive && stat_cache_lookup(conn, name, dirpath, &start, &st)) { *pst = st; return NT_STATUS_OK; } /* * stat the name - if it exists then we are all done! */ if (SMB_VFS_STAT(conn,name,&st) == 0) { /* Ensure we catch all names with in "/." this is disallowed under Windows. */ const char *p = strstr(name, "/."); /* mb safe. */ if (p) { if (p[2] == '/') { /* Error code within a pathname. */ return NT_STATUS_OBJECT_PATH_NOT_FOUND; } else if (p[2] == '\0') { /* Error code at the end of a pathname. */ return NT_STATUS_OBJECT_NAME_INVALID; } } /* * This is a case insensitive file system, we really need to * get the correct case of the name. */ if (!(conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)) { pstring case_preserved_name; if (SMB_VFS_GET_PRESERVED_NAME(conn, name, case_preserved_name)) { char * last_component = strrchr(name, '/'); int space_left = PSTRING_LEN; if (last_component) { last_component++; *last_component = 0; space_left = PSTRING_LEN - strlen(name); } else last_component = name; strlcpy(last_component, case_preserved_name, space_left); } } stat_cache_add(orig_path, name, conn->case_sensitive); DEBUG(5,("conversion finished %s -> %s\n",orig_path, name)); *pst = st; return NT_STATUS_OK; } DEBUG(5,("unix_convert begin: name = %s, dirpath = %s, start = %s\n", name, dirpath, start)); /* * A special case - if we don't have any mangling chars and are case * sensitive then searching won't help. */ if (conn->case_sensitive && !mangle_is_mangled(name, conn->params) && !*lp_mangled_map(conn->params)) { return NT_STATUS_OK; } /* * is_mangled() was changed to look at an entire pathname, not * just a component. JRA. */ if (mangle_is_mangled(start, conn->params)) { component_was_mangled = True; } /* * Now we need to recursively match the name against the real * directory structure. */ /* * Match each part of the path name separately, trying the names * as is first, then trying to scan the directory for matching names. */ for (; start ; start = (end?end+1:(char *)NULL)) { /* * Pinpoint the end of this section of the filename. */ end = strchr(start, '/'); /* mb safe. '/' can't be in any encoded char. */ /* * Chop the name at this point. */ if (end) { *end = 0; } if (saved_last_component != 0) { pstrcpy(saved_last_component, end ? end + 1 : start); } /* The name cannot have a component of "." */ if (ISDOT(start)) { if (!end) { /* Error code at the end of a pathname. */ return NT_STATUS_OBJECT_NAME_INVALID; } return determine_path_error(end+1, allow_wcard_last_component); } /* The name cannot have a wildcard if it's not the last component. */ name_has_wildcard = ms_has_wild(start); /* Wildcard not valid anywhere. */ if (name_has_wildcard && !allow_wcard_last_component) { return NT_STATUS_OBJECT_NAME_INVALID; } /* Wildcards never valid within a pathname. */ if (name_has_wildcard && end) { return NT_STATUS_OBJECT_NAME_INVALID; } /* * Check if the name exists up to this point. */ if (SMB_VFS_STAT(conn,name, &st) == 0) { /* * It exists. it must either be a directory or this must be * the last part of the path for it to be OK. */ if (end && !(st.st_mode & S_IFDIR)) { /* * An intermediate part of the name isn't a directory. */ DEBUG(5,("Not a dir %s\n",start)); *end = '/'; /* * We need to return the fact that the intermediate * name resolution failed. This is used to return an * error of ERRbadpath rather than ERRbadfile. Some * Windows applications depend on the difference between * these two errors. */ return NT_STATUS_OBJECT_PATH_NOT_FOUND; } if (!end) { /* * We just scanned for, and found the end of the path. * We must return the valid stat struct. * JRA. */ *pst = st; } } else { pstring rest; /* Stat failed - ensure we don't use it. */ SET_STAT_INVALID(st); *rest = 0; /* * Remember the rest of the pathname so it can be restored * later. */ if (end) { pstrcpy(rest,end+1); } /* Reset errno so we can detect directory open errors. */ errno = 0; /* * Try to find this part of the path in the directory. */ if (name_has_wildcard || !scan_directory(conn, dirpath, start, sizeof(pstring) - 1 - (start - name))) { if (end) { /* * An intermediate part of the name can't be found. */ DEBUG(5,("Intermediate not found %s\n",start)); *end = '/'; /* * We need to return the fact that the intermediate * name resolution failed. This is used to return an * error of ERRbadpath rather than ERRbadfile. Some * Windows applications depend on the difference between * these two errors. */ /* ENOENT, ENOTDIR and ELOOP all map to * NT_STATUS_OBJECT_PATH_NOT_FOUND * in the filename walk. */ if (errno == ENOENT || errno == ENOTDIR || errno == ELOOP) { return NT_STATUS_OBJECT_PATH_NOT_FOUND; } return map_nt_error_from_unix(errno); } /* ENOENT is the only valid error here. */ if (errno != ENOENT) { /* ENOTDIR and ELOOP both map to * NT_STATUS_OBJECT_PATH_NOT_FOUND * in the filename walk. */ if (errno == ENOTDIR || errno == ELOOP) { return NT_STATUS_OBJECT_PATH_NOT_FOUND; } return map_nt_error_from_unix(errno); } /* * Just the last part of the name doesn't exist. * We need to strupper() or strlower() it as * this conversion may be used for file creation * purposes. Fix inspired by Thomas Neumann <*****@*****.**>. */ if (!conn->case_preserve || (mangle_is_8_3(start, False, conn->params) && !conn->short_case_preserve)) { strnorm(start, lp_defaultcase(SNUM(conn))); } /* * check on the mangled stack to see if we can recover the * base of the filename. */ if (mangle_is_mangled(start, conn->params)) { mangle_check_cache( start, sizeof(pstring) - 1 - (start - name), conn->params); } DEBUG(5,("New file %s\n",start)); return NT_STATUS_OK; } /* * Restore the rest of the string. If the string was mangled the size * may have changed. */ if (end) { end = start + strlen(start); if (!safe_strcat(start, "/", sizeof(pstring) - 1 - (start - name)) || !safe_strcat(start, rest, sizeof(pstring) - 1 - (start - name))) { return map_nt_error_from_unix(ENAMETOOLONG); } *end = '\0'; } else { /* * We just scanned for, and found the end of the path. * We must return a valid stat struct if it exists. * JRA. */ if (SMB_VFS_STAT(conn,name, &st) == 0) { *pst = st; } else { SET_STAT_INVALID(st); } } } /* end else */ #ifdef DEVELOPER if (VALID_STAT(st) && get_delete_on_close_flag(st.st_dev, st.st_ino)) { return NT_STATUS_DELETE_PENDING; } #endif /* * Add to the dirpath that we have resolved so far. */ if (*dirpath) { pstrcat(dirpath,"/"); } pstrcat(dirpath,start); /* * Don't cache a name with mangled or wildcard components * as this can change the size. */ if(!component_was_mangled && !name_has_wildcard) { stat_cache_add(orig_path, dirpath, conn->case_sensitive); } /* * Restore the / that we wiped out earlier. */ if (end) { *end = '/'; } } /* * Don't cache a name with mangled or wildcard components * as this can change the size. */ if(!component_was_mangled && !name_has_wildcard) { stat_cache_add(orig_path, name, conn->case_sensitive); } /* * The name has been resolved. */ DEBUG(5,("conversion finished %s -> %s\n",orig_path, name)); return NT_STATUS_OK; }
/** * Go through all the steps to validate a filename. * * @param ctx talloc_ctx to allocate memory with. * @param conn connection struct for vfs calls. * @param dfs_path Whether this path requires dfs resolution. * @param name_in The unconverted name. * @param ucf_flags flags to pass through to unix_convert(). * UCF_ALWAYS_ALLOW_WCARD_LCOMP will be OR'd in if * p_cont_wcard != NULL and is true and * UCF_COND_ALLOW_WCARD_LCOMP. * @param p_cont_wcard If not NULL, will be set to true if the dfs path * resolution detects a wildcard. * @param pp_smb_fname The final converted name will be allocated if the * return is NT_STATUS_OK. * * @return NT_STATUS_OK if all operations completed succesfully, appropriate * error otherwise. */ NTSTATUS filename_convert(TALLOC_CTX *ctx, connection_struct *conn, bool dfs_path, const char *name_in, uint32_t ucf_flags, bool *ppath_contains_wcard, struct smb_filename **pp_smb_fname) { NTSTATUS status; bool allow_wcards = (ucf_flags & (UCF_COND_ALLOW_WCARD_LCOMP|UCF_ALWAYS_ALLOW_WCARD_LCOMP)); char *fname = NULL; *pp_smb_fname = NULL; status = resolve_dfspath_wcard(ctx, conn, dfs_path, name_in, allow_wcards, &fname, ppath_contains_wcard); if (!NT_STATUS_IS_OK(status)) { DEBUG(10,("filename_convert: resolve_dfspath failed " "for name %s with %s\n", name_in, nt_errstr(status) )); return status; } if (is_fake_file_path(name_in)) { SMB_STRUCT_STAT st; ZERO_STRUCT(st); st.st_ex_nlink = 1; status = create_synthetic_smb_fname_split(ctx, name_in, &st, pp_smb_fname); return status; } /* * If the caller conditionally allows wildcard lookups, only add the * always allow if the path actually does contain a wildcard. */ if (ucf_flags & UCF_COND_ALLOW_WCARD_LCOMP && ppath_contains_wcard != NULL && *ppath_contains_wcard) { ucf_flags |= UCF_ALWAYS_ALLOW_WCARD_LCOMP; } status = unix_convert(ctx, conn, fname, pp_smb_fname, ucf_flags); if (!NT_STATUS_IS_OK(status)) { DEBUG(10,("filename_convert: unix_convert failed " "for name %s with %s\n", fname, nt_errstr(status) )); return status; } if ((ucf_flags & UCF_UNIX_NAME_LOOKUP) && VALID_STAT((*pp_smb_fname)->st) && S_ISLNK((*pp_smb_fname)->st.st_ex_mode)) { return check_veto_path(conn, (*pp_smb_fname)->base_name); } status = check_name(conn, (*pp_smb_fname)->base_name); if (!NT_STATUS_IS_OK(status)) { DEBUG(3,("filename_convert: check_name failed " "for name %s with %s\n", smb_fname_str_dbg(*pp_smb_fname), nt_errstr(status) )); TALLOC_FREE(*pp_smb_fname); return status; } return status; }
NTSTATUS unix_convert(TALLOC_CTX *ctx, connection_struct *conn, const char *orig_path, struct smb_filename **smb_fname_out, uint32_t ucf_flags) { struct smb_filename *smb_fname = NULL; char *start, *end; char *dirpath = NULL; char *stream = NULL; bool component_was_mangled = False; bool name_has_wildcard = False; bool posix_pathnames = false; bool allow_wcard_last_component = (ucf_flags & UCF_ALWAYS_ALLOW_WCARD_LCOMP); bool save_last_component = ucf_flags & UCF_SAVE_LCOMP; NTSTATUS status; int ret = -1; *smb_fname_out = NULL; smb_fname = talloc_zero(ctx, struct smb_filename); if (smb_fname == NULL) { return NT_STATUS_NO_MEMORY; } if (conn->printer) { /* we don't ever use the filenames on a printer share as a filename - so don't convert them */ if (!(smb_fname->base_name = talloc_strdup(smb_fname, orig_path))) { status = NT_STATUS_NO_MEMORY; goto err; } goto done; } DEBUG(5, ("unix_convert called on file \"%s\"\n", orig_path)); /* * Conversion to basic unix format is already done in * check_path_syntax(). */ /* * Names must be relative to the root of the service - any leading /. * and trailing /'s should have been trimmed by check_path_syntax(). */ #ifdef DEVELOPER SMB_ASSERT(*orig_path != '/'); #endif /* * If we trimmed down to a single '\0' character * then we should use the "." directory to avoid * searching the cache, but not if we are in a * printing share. * As we know this is valid we can return true here. */ if (!*orig_path) { if (!(smb_fname->base_name = talloc_strdup(smb_fname, "."))) { status = NT_STATUS_NO_MEMORY; goto err; } if (SMB_VFS_STAT(conn, smb_fname) != 0) { status = map_nt_error_from_unix(errno); goto err; } DEBUG(5, ("conversion finished \"\" -> %s\n", smb_fname->base_name)); goto done; } if (orig_path[0] == '.' && (orig_path[1] == '/' || orig_path[1] == '\0')) { /* Start of pathname can't be "." only. */ if (orig_path[1] == '\0' || orig_path[2] == '\0') { status = NT_STATUS_OBJECT_NAME_INVALID; } else { status =determine_path_error(&orig_path[2], allow_wcard_last_component); } goto err; } /* Start with the full orig_path as given by the caller. */ if (!(smb_fname->base_name = talloc_strdup(smb_fname, orig_path))) { DEBUG(0, ("talloc_strdup failed\n")); status = NT_STATUS_NO_MEMORY; goto err; } /* * Large directory fix normalization. If we're case sensitive, and * the case preserving parameters are set to "no", normalize the case of * the incoming filename from the client WHETHER IT EXISTS OR NOT ! * This is in conflict with the current (3.0.20) man page, but is * what people expect from the "large directory howto". I'll update * the man page. Thanks to [email protected] for finding this. JRA. */ if (conn->case_sensitive && !conn->case_preserve && !conn->short_case_preserve) { strnorm(smb_fname->base_name, lp_defaultcase(SNUM(conn))); } /* * Ensure saved_last_component is valid even if file exists. */ if(save_last_component) { end = strrchr_m(smb_fname->base_name, '/'); if (end) { smb_fname->original_lcomp = talloc_strdup(smb_fname, end + 1); } else { smb_fname->original_lcomp = talloc_strdup(smb_fname, smb_fname->base_name); } if (smb_fname->original_lcomp == NULL) { status = NT_STATUS_NO_MEMORY; goto err; } } posix_pathnames = (lp_posix_pathnames() || (ucf_flags & UCF_POSIX_PATHNAMES)); /* * Strip off the stream, and add it back when we're done with the * base_name. */ if (!posix_pathnames) { stream = strchr_m(smb_fname->base_name, ':'); if (stream != NULL) { char *tmp = talloc_strdup(smb_fname, stream); if (tmp == NULL) { status = NT_STATUS_NO_MEMORY; goto err; } /* * Since this is actually pointing into * smb_fname->base_name this truncates base_name. */ *stream = '\0'; stream = tmp; } } start = smb_fname->base_name; /* * If we're providing case insensitive semantics or * the underlying filesystem is case insensitive, * then a case-normalized hit in the stat-cache is * authoratitive. JRA. * * Note: We're only checking base_name. The stream_name will be * added and verified in build_stream_path(). */ if((!conn->case_sensitive || !(conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)) && stat_cache_lookup(conn, posix_pathnames, &smb_fname->base_name, &dirpath, &start, &smb_fname->st)) { goto done; } /* * Make sure "dirpath" is an allocated string, we use this for * building the directories with talloc_asprintf and free it. */ if ((dirpath == NULL) && (!(dirpath = talloc_strdup(ctx,"")))) { DEBUG(0, ("talloc_strdup failed\n")); status = NT_STATUS_NO_MEMORY; goto err; } /* * If we have a wildcard we must walk the path to * find where the error is, even if case sensitive * is true. */ name_has_wildcard = ms_has_wild(smb_fname->base_name); if (name_has_wildcard && !allow_wcard_last_component) { /* Wildcard not valid anywhere. */ status = NT_STATUS_OBJECT_NAME_INVALID; goto fail; } DEBUG(5,("unix_convert begin: name = %s, dirpath = %s, start = %s\n", smb_fname->base_name, dirpath, start)); if (!name_has_wildcard) { /* * stat the name - if it exists then we can add the stream back (if * there was one) and be done! */ if (posix_pathnames) { ret = SMB_VFS_LSTAT(conn, smb_fname); } else { ret = SMB_VFS_STAT(conn, smb_fname); } if (ret == 0) { status = check_for_dot_component(smb_fname); if (!NT_STATUS_IS_OK(status)) { goto fail; } /* Add the path (not including the stream) to the cache. */ stat_cache_add(orig_path, smb_fname->base_name, conn->case_sensitive); DEBUG(5,("conversion of base_name finished %s -> %s\n", orig_path, smb_fname->base_name)); goto done; } /* Stat failed - ensure we don't use it. */ SET_STAT_INVALID(smb_fname->st); if (errno == ENOENT) { /* Optimization when creating a new file - only the last component doesn't exist. */ status = check_parent_exists(ctx, conn, posix_pathnames, smb_fname, &dirpath, &start); if (!NT_STATUS_IS_OK(status)) { goto fail; } } /* * A special case - if we don't have any wildcards or mangling chars and are case * sensitive or the underlying filesystem is case insensitive then searching * won't help. */ if ((conn->case_sensitive || !(conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)) && !mangle_is_mangled(smb_fname->base_name, conn->params)) { status = check_for_dot_component(smb_fname); if (!NT_STATUS_IS_OK(status)) { goto fail; } /* * The stat failed. Could be ok as it could be * a new file. */ if (errno == ENOTDIR || errno == ELOOP) { status = NT_STATUS_OBJECT_PATH_NOT_FOUND; goto fail; } else if (errno == ENOENT) { /* * Was it a missing last component ? * or a missing intermediate component ? */ struct smb_filename parent_fname; const char *last_component = NULL; ZERO_STRUCT(parent_fname); if (!parent_dirname(ctx, smb_fname->base_name, &parent_fname.base_name, &last_component)) { status = NT_STATUS_NO_MEMORY; goto fail; } if (posix_pathnames) { ret = SMB_VFS_LSTAT(conn, &parent_fname); } else { ret = SMB_VFS_STAT(conn, &parent_fname); } if (ret == -1) { if (errno == ENOTDIR || errno == ENOENT || errno == ELOOP) { status = NT_STATUS_OBJECT_PATH_NOT_FOUND; goto fail; } } /* * Missing last component is ok - new file. * Also deal with permission denied elsewhere. * Just drop out to done. */ goto done; } } } else { /* * We have a wildcard in the pathname. * * Optimization for common case where the wildcard * is in the last component and the client already * sent the correct case. */ status = check_parent_exists(ctx, conn, posix_pathnames, smb_fname, &dirpath, &start); if (!NT_STATUS_IS_OK(status)) { goto fail; } } /* * is_mangled() was changed to look at an entire pathname, not * just a component. JRA. */ if (mangle_is_mangled(start, conn->params)) { component_was_mangled = True; } /* * Now we need to recursively match the name against the real * directory structure. */ /* * Match each part of the path name separately, trying the names * as is first, then trying to scan the directory for matching names. */ for (; start ; start = (end?end+1:(char *)NULL)) { /* * Pinpoint the end of this section of the filename. */ /* mb safe. '/' can't be in any encoded char. */ end = strchr(start, '/'); /* * Chop the name at this point. */ if (end) { *end = 0; } if (save_last_component) { TALLOC_FREE(smb_fname->original_lcomp); smb_fname->original_lcomp = talloc_strdup(smb_fname, end ? end + 1 : start); if (!smb_fname->original_lcomp) { DEBUG(0, ("talloc failed\n")); status = NT_STATUS_NO_MEMORY; goto err; } } /* The name cannot have a component of "." */ if (ISDOT(start)) { if (!end) { /* Error code at the end of a pathname. */ status = NT_STATUS_OBJECT_NAME_INVALID; } else { status = determine_path_error(end+1, allow_wcard_last_component); } goto fail; } /* The name cannot have a wildcard if it's not the last component. */ name_has_wildcard = ms_has_wild(start); /* Wildcards never valid within a pathname. */ if (name_has_wildcard && end) { status = NT_STATUS_OBJECT_NAME_INVALID; goto fail; } /* Skip the stat call if it's a wildcard end. */ if (name_has_wildcard) { DEBUG(5,("Wildcard %s\n",start)); goto done; } /* * Check if the name exists up to this point. */ if (posix_pathnames) { ret = SMB_VFS_LSTAT(conn, smb_fname); } else { ret = SMB_VFS_STAT(conn, smb_fname); } if (ret == 0) { /* * It exists. it must either be a directory or this must * be the last part of the path for it to be OK. */ if (end && !S_ISDIR(smb_fname->st.st_ex_mode)) { /* * An intermediate part of the name isn't * a directory. */ DEBUG(5,("Not a dir %s\n",start)); *end = '/'; /* * We need to return the fact that the * intermediate name resolution failed. This * is used to return an error of ERRbadpath * rather than ERRbadfile. Some Windows * applications depend on the difference between * these two errors. */ status = NT_STATUS_OBJECT_PATH_NOT_FOUND; goto fail; } } else { char *found_name = NULL; /* Stat failed - ensure we don't use it. */ SET_STAT_INVALID(smb_fname->st); /* * Reset errno so we can detect * directory open errors. */ errno = 0; /* * Try to find this part of the path in the directory. */ if (name_has_wildcard || (get_real_filename(conn, dirpath, start, talloc_tos(), &found_name) == -1)) { char *unmangled; if (end) { /* * An intermediate part of the name * can't be found. */ DEBUG(5,("Intermediate not found %s\n", start)); *end = '/'; /* * We need to return the fact that the * intermediate name resolution failed. * This is used to return an error of * ERRbadpath rather than ERRbadfile. * Some Windows applications depend on * the difference between these two * errors. */ /* * ENOENT, ENOTDIR and ELOOP all map * to NT_STATUS_OBJECT_PATH_NOT_FOUND * in the filename walk. */ if (errno == ENOENT || errno == ENOTDIR || errno == ELOOP) { status = NT_STATUS_OBJECT_PATH_NOT_FOUND; } else { status = map_nt_error_from_unix(errno); } goto fail; } /* * ENOENT/EACCESS are the only valid errors * here. EACCESS needs handling here for * "dropboxes", i.e. directories where users * can only put stuff with permission -wx. */ if ((errno != 0) && (errno != ENOENT) && (errno != EACCES)) { /* * ENOTDIR and ELOOP both map to * NT_STATUS_OBJECT_PATH_NOT_FOUND * in the filename walk. */ if (errno == ENOTDIR || errno == ELOOP) { status = NT_STATUS_OBJECT_PATH_NOT_FOUND; } else { status = map_nt_error_from_unix(errno); } goto fail; } /* * Just the last part of the name doesn't exist. * We need to strupper() or strlower() it as * this conversion may be used for file creation * purposes. Fix inspired by * Thomas Neumann <*****@*****.**>. */ if (!conn->case_preserve || (mangle_is_8_3(start, False, conn->params) && !conn->short_case_preserve)) { strnorm(start, lp_defaultcase(SNUM(conn))); } /* * check on the mangled stack to see if we can * recover the base of the filename. */ if (mangle_is_mangled(start, conn->params) && mangle_lookup_name_from_8_3(ctx, start, &unmangled, conn->params)) { char *tmp; size_t start_ofs = start - smb_fname->base_name; if (*dirpath != '\0') { tmp = talloc_asprintf( smb_fname, "%s/%s", dirpath, unmangled); TALLOC_FREE(unmangled); } else { tmp = unmangled; } if (tmp == NULL) { DEBUG(0, ("talloc failed\n")); status = NT_STATUS_NO_MEMORY; goto err; } TALLOC_FREE(smb_fname->base_name); smb_fname->base_name = tmp; start = smb_fname->base_name + start_ofs; end = start + strlen(start); } DEBUG(5,("New file %s\n",start)); goto done; } /* * Restore the rest of the string. If the string was * mangled the size may have changed. */ if (end) { char *tmp; size_t start_ofs = start - smb_fname->base_name; if (*dirpath != '\0') { tmp = talloc_asprintf(smb_fname, "%s/%s/%s", dirpath, found_name, end+1); } else { tmp = talloc_asprintf(smb_fname, "%s/%s", found_name, end+1); } if (tmp == NULL) { DEBUG(0, ("talloc_asprintf failed\n")); status = NT_STATUS_NO_MEMORY; goto err; } TALLOC_FREE(smb_fname->base_name); smb_fname->base_name = tmp; start = smb_fname->base_name + start_ofs; end = start + strlen(found_name); *end = '\0'; } else { char *tmp; size_t start_ofs = start - smb_fname->base_name; if (*dirpath != '\0') { tmp = talloc_asprintf(smb_fname, "%s/%s", dirpath, found_name); } else { tmp = talloc_strdup(smb_fname, found_name); } if (tmp == NULL) { DEBUG(0, ("talloc failed\n")); status = NT_STATUS_NO_MEMORY; goto err; } TALLOC_FREE(smb_fname->base_name); smb_fname->base_name = tmp; start = smb_fname->base_name + start_ofs; /* * We just scanned for, and found the end of * the path. We must return a valid stat struct * if it exists. JRA. */ if (posix_pathnames) { ret = SMB_VFS_LSTAT(conn, smb_fname); } else { ret = SMB_VFS_STAT(conn, smb_fname); } if (ret != 0) { SET_STAT_INVALID(smb_fname->st); } } TALLOC_FREE(found_name); } /* end else */ #ifdef DEVELOPER /* * This sucks! * We should never provide different behaviors * depending on DEVELOPER!!! */ if (VALID_STAT(smb_fname->st)) { bool delete_pending; uint32_t name_hash; status = file_name_hash(conn, smb_fname_str_dbg(smb_fname), &name_hash); if (!NT_STATUS_IS_OK(status)) { goto fail; } get_file_infos(vfs_file_id_from_sbuf(conn, &smb_fname->st), name_hash, &delete_pending, NULL); if (delete_pending) { status = NT_STATUS_DELETE_PENDING; goto fail; } } #endif /* * Add to the dirpath that we have resolved so far. */ if (*dirpath != '\0') { char *tmp = talloc_asprintf(ctx, "%s/%s", dirpath, start); if (!tmp) { DEBUG(0, ("talloc_asprintf failed\n")); status = NT_STATUS_NO_MEMORY; goto err; } TALLOC_FREE(dirpath); dirpath = tmp; } else { TALLOC_FREE(dirpath); if (!(dirpath = talloc_strdup(ctx,start))) { DEBUG(0, ("talloc_strdup failed\n")); status = NT_STATUS_NO_MEMORY; goto err; } } /* * Cache the dirpath thus far. Don't cache a name with mangled * or wildcard components as this can change the size. */ if(!component_was_mangled && !name_has_wildcard) { stat_cache_add(orig_path, dirpath, conn->case_sensitive); } /* * Restore the / that we wiped out earlier. */ if (end) { *end = '/'; } } /* * Cache the full path. Don't cache a name with mangled or wildcard * components as this can change the size. */ if(!component_was_mangled && !name_has_wildcard) { stat_cache_add(orig_path, smb_fname->base_name, conn->case_sensitive); } /* * The name has been resolved. */ DEBUG(5,("conversion finished %s -> %s\n", orig_path, smb_fname->base_name)); done: /* Add back the stream if one was stripped off originally. */ if (stream != NULL) { smb_fname->stream_name = stream; /* Check path now that the base_name has been converted. */ status = build_stream_path(ctx, conn, orig_path, smb_fname); if (!NT_STATUS_IS_OK(status)) { goto fail; } } TALLOC_FREE(dirpath); *smb_fname_out = smb_fname; return NT_STATUS_OK; fail: DEBUG(10, ("dirpath = [%s] start = [%s]\n", dirpath, start)); if (*dirpath != '\0') { smb_fname->base_name = talloc_asprintf(smb_fname, "%s/%s", dirpath, start); } else { smb_fname->base_name = talloc_strdup(smb_fname, start); } if (!smb_fname->base_name) { DEBUG(0, ("talloc_asprintf failed\n")); status = NT_STATUS_NO_MEMORY; goto err; } *smb_fname_out = smb_fname; TALLOC_FREE(dirpath); return status; err: TALLOC_FREE(smb_fname); return status; }
static bool get_ea_dos_attribute(connection_struct *conn, struct smb_filename *smb_fname, uint32 *pattr) { struct xattr_DOSATTRIB dosattrib; enum ndr_err_code ndr_err; DATA_BLOB blob; ssize_t sizeret; fstring attrstr; uint32_t dosattr; if (!lp_store_dos_attributes(SNUM(conn))) { return False; } /* Don't reset pattr to zero as we may already have filename-based attributes we need to preserve. */ sizeret = SMB_VFS_GETXATTR(conn, smb_fname->base_name, SAMBA_XATTR_DOS_ATTRIB, attrstr, sizeof(attrstr)); if (sizeret == -1) { if (errno == ENOSYS #if defined(ENOTSUP) || errno == ENOTSUP) { #else ) { #endif DEBUG(1,("get_ea_dos_attribute: Cannot get attribute " "from EA on file %s: Error = %s\n", smb_fname_str_dbg(smb_fname), strerror(errno))); set_store_dos_attributes(SNUM(conn), False); } return False; } blob.data = (uint8_t *)attrstr; blob.length = sizeret; ndr_err = ndr_pull_struct_blob(&blob, talloc_tos(), &dosattrib, (ndr_pull_flags_fn_t)ndr_pull_xattr_DOSATTRIB); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { DEBUG(1,("get_ea_dos_attribute: bad ndr decode " "from EA on file %s: Error = %s\n", smb_fname_str_dbg(smb_fname), ndr_errstr(ndr_err))); return false; } DEBUG(10,("get_ea_dos_attribute: %s attr = %s\n", smb_fname_str_dbg(smb_fname), dosattrib.attrib_hex)); switch (dosattrib.version) { case 0xFFFF: dosattr = dosattrib.info.compatinfoFFFF.attrib; break; case 1: dosattr = dosattrib.info.info1.attrib; if (!null_nttime(dosattrib.info.info1.create_time)) { struct timespec create_time = nt_time_to_unix_timespec( dosattrib.info.info1.create_time); update_stat_ex_create_time(&smb_fname->st, create_time); DEBUG(10,("get_ea_dos_attribute: file %s case 1 " "set btime %s\n", smb_fname_str_dbg(smb_fname), time_to_asc(convert_timespec_to_time_t( create_time)) )); } break; case 2: dosattr = dosattrib.info.oldinfo2.attrib; /* Don't know what flags to check for this case. */ break; case 3: dosattr = dosattrib.info.info3.attrib; if ((dosattrib.info.info3.valid_flags & XATTR_DOSINFO_CREATE_TIME) && !null_nttime(dosattrib.info.info3.create_time)) { struct timespec create_time = nt_time_to_unix_timespec( dosattrib.info.info3.create_time); update_stat_ex_create_time(&smb_fname->st, create_time); DEBUG(10,("get_ea_dos_attribute: file %s case 3 " "set btime %s\n", smb_fname_str_dbg(smb_fname), time_to_asc(convert_timespec_to_time_t( create_time)) )); } break; default: DEBUG(1,("get_ea_dos_attribute: Badly formed DOSATTRIB on " "file %s - %s\n", smb_fname_str_dbg(smb_fname), attrstr)); return false; } if (S_ISDIR(smb_fname->st.st_ex_mode)) { dosattr |= FILE_ATTRIBUTE_DIRECTORY; } /* FILE_ATTRIBUTE_SPARSE is valid on get but not on set. */ *pattr = (uint32)(dosattr & (SAMBA_ATTRIBUTES_MASK|FILE_ATTRIBUTE_SPARSE)); DEBUG(8,("get_ea_dos_attribute returning (0x%x)", dosattr)); if (dosattr & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h")); if (dosattr & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r")); if (dosattr & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s")); if (dosattr & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d")); if (dosattr & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a")); DEBUG(8,("\n")); return True; } /**************************************************************************** Set DOS attributes in an EA. Also sets the create time. ****************************************************************************/ static bool set_ea_dos_attribute(connection_struct *conn, struct smb_filename *smb_fname, uint32 dosmode) { struct xattr_DOSATTRIB dosattrib; enum ndr_err_code ndr_err; DATA_BLOB blob; ZERO_STRUCT(dosattrib); ZERO_STRUCT(blob); dosattrib.version = 3; dosattrib.info.info3.valid_flags = XATTR_DOSINFO_ATTRIB| XATTR_DOSINFO_CREATE_TIME; dosattrib.info.info3.attrib = dosmode; dosattrib.info.info3.create_time = unix_timespec_to_nt_time( smb_fname->st.st_ex_btime); DEBUG(10,("set_ea_dos_attributes: set attribute 0x%x, btime = %s on file %s\n", (unsigned int)dosmode, time_to_asc(convert_timespec_to_time_t(smb_fname->st.st_ex_btime)), smb_fname_str_dbg(smb_fname) )); ndr_err = ndr_push_struct_blob( &blob, talloc_tos(), &dosattrib, (ndr_push_flags_fn_t)ndr_push_xattr_DOSATTRIB); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { DEBUG(5, ("create_acl_blob: ndr_push_xattr_DOSATTRIB failed: %s\n", ndr_errstr(ndr_err))); return false; } if (blob.data == NULL || blob.length == 0) { return false; } if (SMB_VFS_SETXATTR(conn, smb_fname->base_name, SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length, 0) == -1) { bool ret = false; bool need_close = false; files_struct *fsp = NULL; if((errno != EPERM) && (errno != EACCES)) { if (errno == ENOSYS #if defined(ENOTSUP) || errno == ENOTSUP) { #else ) { #endif DEBUG(1,("set_ea_dos_attributes: Cannot set " "attribute EA on file %s: Error = %s\n", smb_fname_str_dbg(smb_fname), strerror(errno) )); set_store_dos_attributes(SNUM(conn), False); } return false; } /* We want DOS semantics, ie allow non owner with write permission to change the bits on a file. Just like file_ntimes below. */ /* Check if we have write access. */ if(!CAN_WRITE(conn) || !lp_dos_filemode(SNUM(conn))) return false; if (!can_write_to_file(conn, smb_fname)) { return false; } /* * We need to get an open file handle to do the * metadata operation under root. */ if (!NT_STATUS_IS_OK(get_file_handle_for_metadata(conn, smb_fname, &fsp, &need_close))) { return false; } become_root(); if (SMB_VFS_FSETXATTR(fsp, SAMBA_XATTR_DOS_ATTRIB, blob.data, blob.length, 0) == 0) { ret = true; } unbecome_root(); if (need_close) { close_file(NULL, fsp, NORMAL_CLOSE); } return ret; } DEBUG(10,("set_ea_dos_attribute: set EA 0x%x on file %s\n", (unsigned int)dosmode, smb_fname_str_dbg(smb_fname))); return true; } /**************************************************************************** Change a unix mode to a dos mode for an ms dfs link. ****************************************************************************/ uint32 dos_mode_msdfs(connection_struct *conn, const struct smb_filename *smb_fname) { uint32 result = 0; DEBUG(8,("dos_mode_msdfs: %s\n", smb_fname_str_dbg(smb_fname))); if (!VALID_STAT(smb_fname->st)) { return 0; } /* First do any modifications that depend on the path name. */ /* hide files with a name starting with a . */ if (lp_hide_dot_files(SNUM(conn))) { const char *p = strrchr_m(smb_fname->base_name, '/'); if (p) { p++; } else { p = smb_fname->base_name; } /* Only . and .. are not hidden. */ if (p[0] == '.' && !((p[1] == '\0') || (p[1] == '.' && p[2] == '\0'))) { result |= FILE_ATTRIBUTE_HIDDEN; } } result |= dos_mode_from_sbuf(conn, smb_fname); /* Optimization : Only call is_hidden_path if it's not already hidden. */ if (!(result & FILE_ATTRIBUTE_HIDDEN) && IS_HIDDEN_PATH(conn, smb_fname->base_name)) { result |= FILE_ATTRIBUTE_HIDDEN; } if (result == 0) { result = FILE_ATTRIBUTE_NORMAL; } result = filter_mode_by_protocol(result); /* * Add in that it is a reparse point */ result |= FILE_ATTRIBUTE_REPARSE_POINT; DEBUG(8,("dos_mode_msdfs returning ")); if (result & FILE_ATTRIBUTE_HIDDEN) DEBUG(8, ("h")); if (result & FILE_ATTRIBUTE_READONLY ) DEBUG(8, ("r")); if (result & FILE_ATTRIBUTE_SYSTEM) DEBUG(8, ("s")); if (result & FILE_ATTRIBUTE_DIRECTORY ) DEBUG(8, ("d")); if (result & FILE_ATTRIBUTE_ARCHIVE ) DEBUG(8, ("a")); if (result & FILE_ATTRIBUTE_SPARSE ) DEBUG(8, ("[sparse]")); DEBUG(8,("\n")); return(result); }
uint32 dos_mode(connection_struct *conn, struct smb_filename *smb_fname) { uint32 result = 0; bool offline; DEBUG(8,("dos_mode: %s\n", smb_fname_str_dbg(smb_fname))); if (!VALID_STAT(smb_fname->st)) { return 0; } /* First do any modifications that depend on the path name. */ /* hide files with a name starting with a . */ if (lp_hide_dot_files(SNUM(conn))) { const char *p = strrchr_m(smb_fname->base_name,'/'); if (p) { p++; } else { p = smb_fname->base_name; } /* Only . and .. are not hidden. */ if (p[0] == '.' && !((p[1] == '\0') || (p[1] == '.' && p[2] == '\0'))) { result |= FILE_ATTRIBUTE_HIDDEN; } } /* Get the DOS attributes from an EA by preference. */ if (!get_ea_dos_attribute(conn, smb_fname, &result)) { result |= dos_mode_from_sbuf(conn, smb_fname); } offline = SMB_VFS_IS_OFFLINE(conn, smb_fname, &smb_fname->st); if (S_ISREG(smb_fname->st.st_ex_mode) && offline) { result |= FILE_ATTRIBUTE_OFFLINE; } if (conn->fs_capabilities & FILE_FILE_COMPRESSION) { bool compressed = false; NTSTATUS status = dos_mode_check_compressed(conn, smb_fname, &compressed); if (NT_STATUS_IS_OK(status) && compressed) { result |= FILE_ATTRIBUTE_COMPRESSED; } } /* Optimization : Only call is_hidden_path if it's not already hidden. */ if (!(result & FILE_ATTRIBUTE_HIDDEN) && IS_HIDDEN_PATH(conn, smb_fname->base_name)) { result |= FILE_ATTRIBUTE_HIDDEN; } if (result == 0) { result = FILE_ATTRIBUTE_NORMAL; } result = filter_mode_by_protocol(result); dos_mode_debug_print(result); return result; }
NTSTATUS vfs_default_durable_disconnect(struct files_struct *fsp, const DATA_BLOB old_cookie, TALLOC_CTX *mem_ctx, DATA_BLOB *new_cookie) { struct connection_struct *conn = fsp->conn; NTSTATUS status; enum ndr_err_code ndr_err; struct vfs_default_durable_cookie cookie; DATA_BLOB new_cookie_blob = data_blob_null; struct share_mode_lock *lck; bool ok; *new_cookie = data_blob_null; ZERO_STRUCT(cookie); ndr_err = ndr_pull_struct_blob(&old_cookie, talloc_tos(), &cookie, (ndr_pull_flags_fn_t)ndr_pull_vfs_default_durable_cookie); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { status = ndr_map_error2ntstatus(ndr_err); return status; } if (strcmp(cookie.magic, VFS_DEFAULT_DURABLE_COOKIE_MAGIC) != 0) { return NT_STATUS_INVALID_PARAMETER; } if (cookie.version != VFS_DEFAULT_DURABLE_COOKIE_VERSION) { return NT_STATUS_INVALID_PARAMETER; } if (!file_id_equal(&fsp->file_id, &cookie.id)) { return NT_STATUS_INVALID_PARAMETER; } if (!BATCH_OPLOCK_TYPE(fsp->oplock_type)) { return NT_STATUS_NOT_SUPPORTED; } /* * For now let it be simple and do not keep * delete on close files durable open */ if (fsp->initial_delete_on_close) { return NT_STATUS_NOT_SUPPORTED; } if (fsp->delete_on_close) { return NT_STATUS_NOT_SUPPORTED; } if (!VALID_STAT(fsp->fsp_name->st)) { return NT_STATUS_NOT_SUPPORTED; } if (!S_ISREG(fsp->fsp_name->st.st_ex_mode)) { return NT_STATUS_NOT_SUPPORTED; } /* Ensure any pending write time updates are done. */ if (fsp->update_write_time_event) { update_write_time_handler(fsp->conn->sconn->ev_ctx, fsp->update_write_time_event, timeval_current(), (void *)fsp); } /* * The above checks are done in mark_share_mode_disconnected() too * but we want to avoid getting the lock if possible */ lck = get_existing_share_mode_lock(talloc_tos(), fsp->file_id); if (lck != NULL) { struct smb_file_time ft; ZERO_STRUCT(ft); if (fsp->write_time_forced) { ft.mtime = lck->data->changed_write_time; } else if (fsp->update_write_time_on_close) { if (null_timespec(fsp->close_write_time)) { ft.mtime = timespec_current(); } else { ft.mtime = fsp->close_write_time; } } if (!null_timespec(ft.mtime)) { round_timespec(conn->ts_res, &ft.mtime); file_ntimes(conn, fsp->fsp_name, &ft); } ok = mark_share_mode_disconnected(lck, fsp); if (!ok) { TALLOC_FREE(lck); } } if (lck != NULL) { ok = brl_mark_disconnected(fsp); if (!ok) { TALLOC_FREE(lck); } } if (lck == NULL) { return NT_STATUS_NOT_SUPPORTED; } TALLOC_FREE(lck); status = vfs_stat_fsp(fsp); if (!NT_STATUS_IS_OK(status)) { return status; } ZERO_STRUCT(cookie); cookie.allow_reconnect = true; cookie.id = fsp->file_id; cookie.servicepath = conn->connectpath; cookie.base_name = fsp->fsp_name->base_name; cookie.initial_allocation_size = fsp->initial_allocation_size; cookie.position_information = fsp->fh->position_information; cookie.update_write_time_triggered = fsp->update_write_time_triggered; cookie.update_write_time_on_close = fsp->update_write_time_on_close; cookie.write_time_forced = fsp->write_time_forced; cookie.close_write_time = fsp->close_write_time; cookie.stat_info.st_ex_dev = fsp->fsp_name->st.st_ex_dev; cookie.stat_info.st_ex_ino = fsp->fsp_name->st.st_ex_ino; cookie.stat_info.st_ex_mode = fsp->fsp_name->st.st_ex_mode; cookie.stat_info.st_ex_nlink = fsp->fsp_name->st.st_ex_nlink; cookie.stat_info.st_ex_uid = fsp->fsp_name->st.st_ex_uid; cookie.stat_info.st_ex_gid = fsp->fsp_name->st.st_ex_gid; cookie.stat_info.st_ex_rdev = fsp->fsp_name->st.st_ex_rdev; cookie.stat_info.st_ex_size = fsp->fsp_name->st.st_ex_size; cookie.stat_info.st_ex_atime = fsp->fsp_name->st.st_ex_atime; cookie.stat_info.st_ex_mtime = fsp->fsp_name->st.st_ex_mtime; cookie.stat_info.st_ex_ctime = fsp->fsp_name->st.st_ex_ctime; cookie.stat_info.st_ex_btime = fsp->fsp_name->st.st_ex_btime; cookie.stat_info.st_ex_calculated_birthtime = fsp->fsp_name->st.st_ex_calculated_birthtime; cookie.stat_info.st_ex_blksize = fsp->fsp_name->st.st_ex_blksize; cookie.stat_info.st_ex_blocks = fsp->fsp_name->st.st_ex_blocks; cookie.stat_info.st_ex_flags = fsp->fsp_name->st.st_ex_flags; cookie.stat_info.st_ex_mask = fsp->fsp_name->st.st_ex_mask; ndr_err = ndr_push_struct_blob(&new_cookie_blob, mem_ctx, &cookie, (ndr_push_flags_fn_t)ndr_push_vfs_default_durable_cookie); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { status = ndr_map_error2ntstatus(ndr_err); return status; } status = fd_close(fsp); if (!NT_STATUS_IS_OK(status)) { data_blob_free(&new_cookie_blob); return status; } *new_cookie = new_cookie_blob; return NT_STATUS_OK; }