/*
* Unix SMB/CIFS implementation.
*
- * This file began with some code from source3/smbd/open.c and modified it to
- * work with ifs_createfile.
+ * This file began with some code from source3/smbd/open.c and has been
+ * modified it work with ifs_createfile.
*
* ifs_createfile is a CIFS-specific syscall for opening/files and
* directories. It adds support for:
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
+#include "includes.h"
#include "onefs.h"
+#include "onefs_config.h"
+#include "oplock_onefs.h"
+#include "smbd/globals.h"
extern const struct generic_mapping file_generic_mapping;
-extern bool global_client_failed_oplock_break;
-struct deferred_open_record {
- bool delayed_for_oplocks;
- bool failed; /* added for onefs_oplocks */
- struct file_id id;
+struct onefs_fsp_data {
+ uint64_t oplock_callback_id;
};
static NTSTATUS onefs_create_file_unixpath(connection_struct *conn,
struct smb_request *req,
- const char *fname,
+ struct smb_filename *smb_fname,
uint32_t access_mask,
uint32_t share_access,
uint32_t create_disposition,
uint64_t allocation_size,
struct security_descriptor *sd,
struct ea_list *ea_list,
-
files_struct **result,
int *pinfo,
- SMB_STRUCT_STAT *psbuf);
+ struct onefs_fsp_data *fsp_data);
/****************************************************************************
Open a file.
connection_struct *conn,
struct smb_request *req,
const char *parent_dir,
- const char *name,
- const char *path,
- SMB_STRUCT_STAT *psbuf,
+ struct smb_filename *smb_fname,
int flags,
mode_t unx_mode,
uint32 access_mask,
struct security_descriptor *sd,
int *granted_oplock)
{
+ char *path = NULL;
+ struct smb_filename *smb_fname_onefs = NULL;
NTSTATUS status = NT_STATUS_OK;
int accmode = (flags & O_ACCMODE);
int local_flags = flags;
- bool file_existed = VALID_STAT(*psbuf);
+ bool file_existed = VALID_STAT(smb_fname->st);
const char *wild;
+ int base_fd = -1;
fsp->fh->fd = -1;
errno = EPERM;
+ status = get_full_smb_filename(talloc_tos(), smb_fname,
+ &path);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
/* Check permissions */
/*
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", path));
+ DEBUG(3, ("Permission denied opening %s\n",
+ smb_fname_str_dbg(smb_fname)));
return NT_STATUS_ACCESS_DENIED;
} else if(flags & O_CREAT) {
/* We don't want to write - but we must make sure that
if ((accmode == O_RDONLY) && ((flags & O_TRUNC) == O_TRUNC)) {
DEBUG(10,("onefs_open_file: truncate requested on read-only "
- "open for file %s\n", path));
+ "open for file %s\n", smb_fname_str_dbg(smb_fname)));
local_flags = (flags & ~O_ACCMODE)|O_RDWR;
}
* open flags. JRA.
*/
- if (file_existed && S_ISFIFO(psbuf->st_mode)) {
+ if (file_existed && S_ISFIFO(smb_fname->st.st_ex_mode)) {
local_flags |= O_NONBLOCK;
}
#endif
* then was copied via Samba.
*/
DEBUG(1, ("onefs_open_file: creating file with wildcard: %s\n",
- path));
+ smb_fname_str_dbg(smb_fname)));
return NT_STATUS_OBJECT_NAME_INVALID;
}
flags |= O_NOFOLLOW;
}
#endif
- /* Don't request an oplock if oplocks are turned off for the
- * share. */
- if (!lp_oplocks(SNUM(conn)))
- oplock_request = 0;
+ /* Setup a onefs-style smb_fname struct. */
+ status = onefs_stream_prep_smb_fname(talloc_tos(), smb_fname,
+ &smb_fname_onefs);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* If it's a stream pass in the base_fd */
+ if ((conn->fs_capabilities & FILE_NAMED_STREAMS) &&
+ is_ntfs_stream_smb_fname(smb_fname_onefs)) {
+ SMB_ASSERT(fsp->base_fsp);
+
+ DEBUG(10, ("Opening a stream: base=%s(%d), stream=%s\n",
+ smb_fname_onefs->base_name, fsp->base_fsp->fh->fd,
+ smb_fname_onefs->stream_name));
+
+ base_fd = fsp->base_fsp->fh->fd;
+ }
fsp->fh->fd = onefs_sys_create_file(conn,
- -1,
- path,
+ base_fd,
+ smb_fname_onefs->stream_name != NULL ?
+ smb_fname_onefs->stream_name :
+ smb_fname_onefs->base_name,
access_mask,
open_access_mask,
share_access,
sd,
new_dos_attributes,
granted_oplock);
+ TALLOC_FREE(smb_fname_onefs);
if (fsp->fh->fd == -1) {
if (errno == EMFILE) {
}
status = map_nt_error_from_unix(errno);
- DEBUG(3,("Error opening file %s (%s) (local_flags=%d) "
- "(flags=%d)\n",
- path,nt_errstr(status),local_flags,flags));
+ DEBUG(3, ("Error opening file %s (%s) (local_flags=%d) "
+ "(flags=%d)\n", smb_fname_str_dbg(smb_fname),
+ strerror(errno), local_flags, flags));
return status;
}
int ret;
if (fsp->fh->fd == -1) {
- ret = SMB_VFS_STAT(conn, path, psbuf);
+ ret = SMB_VFS_STAT(conn, smb_fname);
} else {
- ret = SMB_VFS_FSTAT(fsp, psbuf);
+ ret = SMB_VFS_FSTAT(fsp, &smb_fname->st);
/* If we have an fd, this stat should succeed. */
if (ret == -1) {
- DEBUG(0,("Error doing fstat on open file %s "
- "(%s)\n", path,strerror(errno) ));
+ DEBUG(0, ("Error doing fstat on open file %s "
+ "(%s)\n",
+ smb_fname_str_dbg(smb_fname),
+ strerror(errno) ));
}
}
* so catch a directory open and return an EISDIR. JRA.
*/
- if(S_ISDIR(psbuf->st_mode)) {
+ if(S_ISDIR(smb_fname->st.st_ex_mode)) {
fd_close(fsp);
errno = EISDIR;
return NT_STATUS_FILE_IS_A_DIRECTORY;
}
- fsp->mode = psbuf->st_mode;
- fsp->file_id = vfs_file_id_from_sbuf(conn, psbuf);
+ fsp->mode = smb_fname->st.st_ex_mode;
+ fsp->file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
fsp->vuid = req ? req->vuid : UID_FIELD_INVALID;
fsp->file_pid = req ? req->smbpid : 0;
fsp->can_lock = True;
exit_server("push_deferred_smb_message failed");
}
add_deferred_open(lck, req->mid, request_time, state->id);
-
- /*
- * Push the MID of this packet on the signing queue.
- * We only do this once, the first time we push the packet
- * onto the deferred open queue, as this has a side effect
- * of incrementing the response sequence number.
- */
-
- srv_defer_sign_response(req->mid);
}
static void schedule_defer_open(struct share_mode_lock *lck,
* measure here in case the other smbd is stuck
* somewhere else. */
- timeout = timeval_set(OPLOCK_BREAK_TIMEOUT*2, 0);
+ /*
+ * On OneFS, the kernel will always send an oplock_revoked message
+ * before this timeout is hit.
+ */
+ timeout = timeval_set(OPLOCK_BREAK_TIMEOUT*10, 0);
/* Nothing actually uses state.delayed_for_oplocks
but it's handy to differentiate in debug messages
a 1 second delay for share mode conflicts. */
state.delayed_for_oplocks = True;
- state.failed = False;
+ state.failed = false;
state.id = lck->id;
if (!request_timed_out(request_time, timeout)) {
****************************************************************************/
NTSTATUS onefs_open_file_ntcreate(connection_struct *conn,
struct smb_request *req,
- const char *fname,
+ struct smb_filename *smb_fname,
uint32 access_mask,
uint32 share_access,
uint32 create_disposition,
struct security_descriptor *sd,
files_struct *fsp,
int *pinfo,
- SMB_STRUCT_STAT *psbuf)
+ struct onefs_fsp_data *fsp_data)
{
int flags=0;
int flags2=0;
- bool file_existed = VALID_STAT(*psbuf);
+ bool file_existed = VALID_STAT(smb_fname->st);
bool def_acl = False;
bool posix_open = False;
bool new_file_created = False;
+ bool clear_ads = False;
struct file_id id;
mode_t new_unx_mode = (mode_t)0;
mode_t unx_mode = (mode_t)0;
NTSTATUS status;
int ret_flock;
char *parent_dir;
- const char *newname;
int granted_oplock;
- uint64 oplock_waiter;
+ uint64_t oplock_callback_id = 0;
uint32 createfile_attributes = 0;
+ char *fname = NULL;
ZERO_STRUCT(id);
+ status = get_full_smb_filename(talloc_tos(), smb_fname,
+ &fname);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
if (conn->printer) {
/*
* Printers are handled completely differently.
}
DEBUG(10, ("onefs_open_file_ntcreate: printer open fname=%s\n",
- fname));
+ smb_fname_str_dbg(smb_fname)));
- return print_fsp_open(req, conn, fname, req->vuid, fsp);
+ return print_fsp_open(req, conn, fname, req->vuid, fsp,
+ &smb_fname->st);
}
- if (!parent_dirname_talloc(talloc_tos(), fname, &parent_dir,
- &newname)) {
+ if (!parent_dirname(talloc_tos(), smb_fname->base_name, &parent_dir,
+ NULL)) {
return NT_STATUS_NO_MEMORY;
}
"access_mask=0x%x share_access=0x%x "
"create_disposition = 0x%x create_options=0x%x "
"unix mode=0%o oplock_request=0x%x\n",
- fname, new_dos_attributes, access_mask, share_access,
- create_disposition, create_options, unx_mode,
- oplock_request));
+ smb_fname_str_dbg(smb_fname), new_dos_attributes,
+ access_mask, share_access, create_disposition,
+ create_options, unx_mode, oplock_request));
+
+ /*
+ * Any non-stat-only open has the potential to contend oplocks, which
+ * means to avoid blocking in the kernel (which is unacceptable), the
+ * open must be deferred. In order to defer opens, req must not be
+ * NULL. The known cases of calling with a NULL req:
+ *
+ * 1. Open the base file of a stream: Always done stat-only
+ *
+ * 2. open_file_fchmod(), which is called from 3 places:
+ * A. try_chown: Posix acls only. Never called on onefs.
+ * B. set_ea_dos_attributes: Can't be called from onefs, because
+ * SMB_VFS_SETXATTR return ENOSYS.
+ * C. file_set_dos_mode: This would only happen if the "dos
+ * filemode" smb.conf parameter is set to yes. We ship with
+ * it off, but if a customer were to turn it on it would be
+ * bad.
+ */
+ if (req == NULL && !is_stat_open(access_mask) &&
+ !is_ntfs_stream_smb_fname(smb_fname)) {
+ smb_panic("NULL req on a non-stat-open!");
+ }
if ((req == NULL) && ((oplock_request & INTERNAL_OPEN_ONLY) == 0)) {
DEBUG(0, ("No smb request but not an internal only open!\n"));
}
}
- status = check_name(conn, fname);
+ status = check_name(conn, smb_fname->base_name);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
if (!posix_open) {
new_dos_attributes &= SAMBA_ATTRIBUTES_MASK;
if (file_existed) {
- existing_dos_attributes = dos_mode(conn, fname, psbuf);
+ existing_dos_attributes = dos_mode(conn, fname,
+ &smb_fname->st);
}
}
/* Ignore oplock requests if oplocks are disabled. */
if (!lp_oplocks(SNUM(conn)) || global_client_failed_oplock_break ||
- IS_VETO_OPLOCK_PATH(conn, fname)) {
+ IS_VETO_OPLOCK_PATH(conn, smb_fname->base_name)) {
/* Mask off everything except the private Samba bits. */
oplock_request &= SAMBA_PRIVATE_OPLOCK_MASK;
}
/* this is for OS/2 long file names - say we don't support them */
- if (!lp_posix_pathnames() && strstr(fname,".+,;=[].")) {
+ if (!lp_posix_pathnames() && strstr(smb_fname->base_name,".+,;=[].")) {
/* OS/2 Workplace shell fix may be main code stream in a later
* release. */
DEBUG(5,("onefs_open_file_ntcreate: OS/2 long filenames are "
* (requiring delete access) then recreates it.
*/
case FILE_SUPERSEDE:
- /* If file exists replace/overwrite. If file doesn't
- * exist create. */
/**
* @todo: Clear all file attributes?
* http://www.osronline.com/article.cfm?article=302
* exist create.
*/
flags2 |= (O_CREAT | O_TRUNC);
+ clear_ads = true;
break;
case FILE_OVERWRITE_IF:
/* If file exists replace/overwrite. If file doesn't
* exist create. */
flags2 |= (O_CREAT | O_TRUNC);
+ clear_ads = true;
break;
case FILE_OPEN:
if (!file_existed) {
DEBUG(5,("onefs_open_file_ntcreate: FILE_OPEN "
"requested for file %s and file "
- "doesn't exist.\n", fname ));
+ "doesn't exist.\n",
+ smb_fname_str_dbg(smb_fname)));
errno = ENOENT;
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
DEBUG(5, ("onefs_open_file_ntcreate: "
"FILE_OVERWRITE requested for file "
"%s and file doesn't exist.\n",
- fname));
+ smb_fname_str_dbg(smb_fname)));
errno = ENOENT;
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
flags2 |= O_TRUNC;
+ clear_ads = true;
break;
case FILE_CREATE:
DEBUG(5, ("onefs_open_file_ntcreate: "
"FILE_CREATE requested for file %s "
"and file already exists.\n",
- fname));
- if (S_ISDIR(psbuf->st_mode)) {
+ smb_fname_str_dbg(smb_fname)));
+ if (S_ISDIR(smb_fname->st.st_ex_mode)) {
errno = EISDIR;
} else {
errno = EEXIST;
if (!posix_open && file_existed &&
((create_disposition == FILE_OVERWRITE) ||
(create_disposition == FILE_OVERWRITE_IF))) {
- if (!open_match_attributes(conn, fname,
- existing_dos_attributes,
- new_dos_attributes, psbuf->st_mode,
+ if (!open_match_attributes(conn, existing_dos_attributes,
+ new_dos_attributes,
+ smb_fname->st.st_ex_mode,
unx_mode, &new_unx_mode)) {
DEBUG(5, ("onefs_open_file_ntcreate: attributes "
"missmatch for file %s (%x %x) (0%o, 0%o)\n",
- fname, existing_dos_attributes,
+ smb_fname_str_dbg(smb_fname),
+ existing_dos_attributes,
new_dos_attributes,
- (unsigned int)psbuf->st_mode,
+ (unsigned int)smb_fname->st.st_ex_mode,
(unsigned int)unx_mode ));
errno = EACCES;
return NT_STATUS_ACCESS_DENIED;
DEBUG(10, ("onefs_open_file_ntcreate: fname=%s, after mapping "
"open_access_mask=%#x, access_mask=0x%x\n",
- fname, open_access_mask, access_mask));
+ smb_fname_str_dbg(smb_fname), open_access_mask,
+ access_mask));
/*
* Note that we ignore the append flag as append does not
*/
flags2 &= ~(O_CREAT|O_TRUNC);
- /**
- * XXX: TODO
- * Apparently this is necessary because we ship with
- * lp_acl_check_permissions = no. It is set to no because our
- * ifs_createfile does the access check correctly. This check
- * was added in the last merge, and the question is why is it
- * necessary? Check out Bug 25547 and Bug 14596. The key is
- * to figure out what case this is covering, and do some
- * testing to see if it's actually necessary. If it is, maybe
- * it should go upstream in open.c.
- */
- if (!lp_acl_check_permissions(SNUM(conn)) &&
- (access_mask & DELETE_ACCESS)) {
+ /* Deny DELETE_ACCESS explicitly if the share is read only. */
+ if (access_mask & DELETE_ACCESS) {
return map_nt_error_from_unix(EACCES);
}
}
(!CAN_WRITE(conn) || IS_DOS_READONLY(existing_dos_attributes))) {
DEBUG(5, ("onefs_open_file_ntcreate: write access requested "
"for file %s on read only %s\n",
- fname, !CAN_WRITE(conn) ? "share" : "file" ));
+ smb_fname_str_dbg(smb_fname),
+ !CAN_WRITE(conn) ? "share" : "file" ));
errno = EACCES;
return NT_STATUS_ACCESS_DENIED;
}
DEBUG(10, ("fsp = %p\n", fsp));
- fsp->file_id = vfs_file_id_from_sbuf(conn, psbuf);
+ fsp->file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
fsp->share_access = share_access;
fsp->fh->private_options = create_options;
fsp->access_mask = open_access_mask; /* We change this to the
}
if (file_existed) {
- struct timespec old_write_time = get_mtimespec(psbuf);
- id = vfs_file_id_from_sbuf(conn, psbuf);
+ struct timespec old_write_time = smb_fname->st.st_ex_mtime;
+ id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
lck = get_share_mode_lock(talloc_tos(), id,
conn->connectpath,
(unsigned int)unx_mode, (unsigned int)access_mask,
(unsigned int)open_access_mask));
- oplock_waiter = 1; //ifs_oplock_wait_record(mid);
-
- if (oplock_waiter == 0) {
- return NT_STATUS_NO_MEMORY;
+ /*
+ * Since the open is guaranteed to be stat only if req == NULL, a
+ * callback record is only needed if req != NULL.
+ */
+ if (req) {
+ SMB_ASSERT(fsp_data);
+ oplock_callback_id = onefs_oplock_wait_record(req->mid);
+ if (oplock_callback_id == 0) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ /*
+ * It is also already asserted it's either a stream or a
+ * stat-only open at this point.
+ */
+ SMB_ASSERT(fsp->oplock_type == NO_OPLOCK);
}
/* Do the open. */
conn,
req,
parent_dir,
- newname,
- fname,
- psbuf,
+ smb_fname,
flags|flags2,
unx_mode,
access_mask,
open_access_mask,
fsp->oplock_type,
- oplock_waiter,
+ oplock_callback_id,
share_access,
create_options,
createfile_attributes,
struct deferred_open_record state;
struct timespec old_write_time;
- old_write_time = get_mtimespec(psbuf);
+ old_write_time = smb_fname->st.st_ex_mtime;
DEBUG(3, ("Someone created file %s with an "
"oplock after we looked: Retrying\n",
- fname));
+ smb_fname_str_dbg(smb_fname)));
/*
* We hit the race that when we did the stat
* on the file it did not exist, and someone
* has created it in between the stat and the
* open_file() call. Just retry immediately.
*/
- id = vfs_file_id_from_sbuf(conn, psbuf);
+ id = vfs_file_id_from_sbuf(conn,
+ &smb_fname->st);
if (!(lck = get_share_mode_lock(talloc_tos(),
id, conn->connectpath, fname,
&old_write_time))) {
*/
DEBUG(0, ("onefs_open_file_ntcreate: "
"Could not get share mode "
- "lock for %s\n", fname));
+ "lock for %s\n",
+ smb_fname_str_dbg(smb_fname)));
status = NT_STATUS_SHARING_VIOLATION;
goto cleanup_destroy;
}
goto cleanup_destroy;
}
/* Waiting for an oplock */
+ DEBUG(5,("Async createfile because a client has an "
+ "oplock on %s\n",
+ smb_fname_str_dbg(smb_fname)));
+
SMB_ASSERT(req);
schedule_defer_open(lck, request_time, req);
goto cleanup;
create_options);
if (NT_STATUS_IS_OK(status)) {
- TALLOC_FREE(lck);
if (pinfo) {
*pinfo = FILE_WAS_OPENED;
}
can_access_mask = FILE_READ_DATA;
}
- if (((can_access_mask & FILE_WRITE_DATA) && !CAN_WRITE(conn)) ||
- !can_access_file_data(conn,fname,psbuf,can_access_mask)) {
+ if (((can_access_mask & FILE_WRITE_DATA) &&
+ !CAN_WRITE(conn)) ||
+ !can_access_file_data(conn, smb_fname,
+ can_access_mask)) {
can_access = False;
}
* Normal error, for example EACCES
*/
cleanup_destroy:
- //destroy_ifs_callback_record(oplock_waiter);
+ if (oplock_callback_id != 0) {
+ destroy_onefs_callback_record(oplock_callback_id);
+ }
cleanup:
TALLOC_FREE(lck);
return status;
fsp->oplock_type = granted_oplock;
- /* XXX uncomment for oplocks */
- //ifs_set_oplock_callback(oplock_waiter, fsp);
- //fsp->oplock_callback_id = oplock_waiter;
+ if (oplock_callback_id != 0) {
+ onefs_set_oplock_callback(oplock_callback_id, fsp);
+ fsp_data->oplock_callback_id = oplock_callback_id;
+ } else {
+ SMB_ASSERT(fsp->oplock_type == NO_OPLOCK);
+ }
if (!file_existed) {
- struct timespec old_write_time = get_mtimespec(psbuf);
+ struct timespec old_write_time = smb_fname->st.st_ex_mtime;
/*
* Deal with the race condition where two smbd's detect the
* file doesn't exist and do the create at the same time. One
if (lck == NULL) {
DEBUG(0, ("onefs_open_file_ntcreate: Could not get "
- "share mode lock for %s\n", fname));
+ "share mode lock for %s\n",
+ smb_fname_str_dbg(smb_fname)));
fd_close(fsp);
return NT_STATUS_SHARING_VIOLATION;
}
SMB_ASSERT(lck != NULL);
+ /* Delete streams if create_disposition requires it */
+ if (file_existed && clear_ads &&
+ !is_ntfs_stream_smb_fname(smb_fname)) {
+ status = delete_all_streams(conn, smb_fname->base_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(lck);
+ fd_close(fsp);
+ return status;
+ }
+ }
+
/* note that we ignore failure for the following. It is
basically a hack for NFS, and NFS will never set one of
these only read them. Nobody but Samba can ever set a deny
}
}
+ if (fsp->oplock_type == LEVEL_II_OPLOCK &&
+ (!lp_level2_oplocks(SNUM(conn)) ||
+ !(global_client_caps & CAP_LEVEL_II_OPLOCKS))) {
+
+ DEBUG(5, ("Downgrading level2 oplock on open "
+ "because level2 oplocks = off\n"));
+
+ release_file_oplock(fsp);
+ }
+
if (info == FILE_WAS_OVERWRITTEN || info == FILE_WAS_CREATED ||
info == FILE_WAS_SUPERSEDED) {
new_file_created = True;
* selected.
* May be necessary depending on acl policies.
*/
- if (!posix_open && !file_existed && !def_acl && !(VALID_STAT(*psbuf)
- && (psbuf->st_flags & SF_HASNTFSACL))) {
+ if (!posix_open && !file_existed && !def_acl &&
+ !(VALID_STAT(smb_fname->st) &&
+ (smb_fname->st.st_ex_flags & SF_HASNTFSACL))) {
int saved_errno = errno; /* We might get ENOSYS in the next
* call.. */
} else {
DEBUG(5, ("onefs_open_file_ntcreate: reset "
"attributes of file %s to 0%o\n",
- fname, (unsigned int)new_unx_mode));
+ smb_fname_str_dbg(smb_fname),
+ (unsigned int)new_unx_mode));
ret = 0; /* Don't do the fchmod below. */
}
}
(SMB_VFS_FCHMOD(fsp, new_unx_mode) == -1))
DEBUG(5, ("onefs_open_file_ntcreate: failed to reset "
"attributes of file %s to 0%o\n",
- fname, (unsigned int)new_unx_mode));
+ smb_fname_str_dbg(smb_fname),
+ (unsigned int)new_unx_mode));
}
/* If this is a successful open, we must remove any deferred open
****************************************************************************/
static NTSTATUS onefs_open_directory(connection_struct *conn,
struct smb_request *req,
- const char *fname,
+ struct smb_filename *smb_dname,
uint32 access_mask,
uint32 share_access,
uint32 create_disposition,
uint32 file_attributes,
struct security_descriptor *sd,
files_struct **result,
- int *pinfo,
- SMB_STRUCT_STAT *psbuf)
+ int *pinfo)
{
files_struct *fsp = NULL;
struct share_mode_lock *lck = NULL;
"access_mask = 0x%x, "
"share_access = 0x%x create_options = 0x%x, "
"create_disposition = 0x%x, file_attributes = 0x%x\n",
- fname, (unsigned int)access_mask, (unsigned int)share_access,
- (unsigned int)create_options, (unsigned int)create_disposition,
+ smb_fname_str_dbg(smb_dname), (unsigned int)access_mask,
+ (unsigned int)share_access, (unsigned int)create_options,
+ (unsigned int)create_disposition,
(unsigned int)file_attributes));
if (!(file_attributes & FILE_FLAG_POSIX_SEMANTICS) &&
(conn->fs_capabilities & FILE_NAMED_STREAMS) &&
- is_ntfs_stream_name(fname)) {
- DEBUG(2, ("onefs_open_directory: %s is a stream name!\n", fname));
+ is_ntfs_stream_smb_fname(smb_dname)) {
+ DEBUG(2, ("onefs_open_directory: %s is a stream name!\n",
+ smb_fname_str_dbg(smb_dname)));
return NT_STATUS_NOT_A_DIRECTORY;
}
default:
DEBUG(5, ("onefs_open_directory: invalid "
"create_disposition 0x%x for directory %s\n",
- (unsigned int)create_disposition, fname));
+ (unsigned int)create_disposition,
+ smb_fname_str_dbg(smb_dname)));
return NT_STATUS_INVALID_PARAMETER;
}
}
/* Get parent dirname */
- if (!parent_dirname_talloc(talloc_tos(), fname, &parent_dir,
- &dirname)) {
+ if (!parent_dirname(talloc_tos(), smb_dname->base_name, &parent_dir,
+ &dirname)) {
return NT_STATUS_NO_MEMORY;
}
mode = (mode_t)(file_attributes & ~FILE_FLAG_POSIX_SEMANTICS);
file_attributes = 0;
} else {
- mode = unix_mode(conn, aDIR, fname, parent_dir);
+ mode = unix_mode(conn, aDIR, smb_dname->base_name, parent_dir);
}
/*
retry_open:
fsp->fh->fd = onefs_sys_create_file(conn,
-1,
- fname,
+ smb_dname->base_name,
access_mask,
access_mask,
share_access,
NULL);
if (fsp->fh->fd == -1) {
- DEBUG(3, ("Error opening %s. Errno=%d (%s).\n", fname, errno,
+ DEBUG(3, ("Error opening %s. Errno=%d (%s).\n",
+ smb_fname_str_dbg(smb_dname), errno,
strerror(errno)));
SMB_ASSERT(errno != EINPROGRESS);
file_free(req, fsp);
if ((errno == ENOENT) && (create_disposition == FILE_OPEN)) {
- DEBUG(5,("onefs_open_directory: FILE_OPEN requested "
+ DEBUG(5, ("onefs_open_directory: FILE_OPEN requested "
"for directory %s and it doesn't "
- "exist.\n", fname ));
+ "exist.\n", smb_fname_str_dbg(smb_dname)));
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
} else if ((errno == EEXIST) &&
(create_disposition == FILE_CREATE)) {
- DEBUG(5,("onefs_open_directory: FILE_CREATE "
+ DEBUG(5, ("onefs_open_directory: FILE_CREATE "
"requested for directory %s and it "
- "already exists.\n", fname ));
+ "already exists.\n",
+ smb_fname_str_dbg(smb_dname)));
return NT_STATUS_OBJECT_NAME_COLLISION;
} else if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) {
/* Catch sharing violations. */
if (info == FILE_WAS_CREATED) {
/* Pulled from mkdir_internal() */
- if (SMB_VFS_LSTAT(conn, fname, psbuf) == -1) {
+ if (SMB_VFS_LSTAT(conn, smb_dname) == -1) {
DEBUG(2, ("Could not stat directory '%s' just "
- "created: %s\n",fname, strerror(errno)));
+ "created: %s\n",
+ smb_fname_str_dbg(smb_dname),
+ strerror(errno)));
return map_nt_error_from_unix(errno);
}
- if (!S_ISDIR(psbuf->st_mode)) {
+ if (!S_ISDIR(smb_dname->st.st_ex_mode)) {
DEBUG(0, ("Directory just '%s' created is not a "
- "directory\n", fname));
+ "directory\n",
+ smb_fname_str_dbg(smb_dname)));
return NT_STATUS_ACCESS_DENIED;
}
* parent dir.
*/
if (mode & ~(S_IRWXU|S_IRWXG|S_IRWXO) &&
- (mode & ~psbuf->st_mode)) {
- SMB_VFS_CHMOD(conn, fname, (psbuf->st_mode |
- (mode & ~psbuf->st_mode)));
+ (mode & ~smb_dname->st.st_ex_mode)) {
+ SMB_VFS_CHMOD(conn, smb_dname->base_name,
+ (smb_dname->st.st_ex_mode |
+ (mode & ~smb_dname->st.st_ex_mode)));
}
}
/* Change the owner if required. */
if (lp_inherit_owner(SNUM(conn))) {
- change_dir_owner_to_parent(conn, parent_dir, fname,
- psbuf);
+ change_dir_owner_to_parent(conn, parent_dir,
+ smb_dname->base_name,
+ &smb_dname->st);
}
notify_fname(conn, NOTIFY_ACTION_ADDED,
- FILE_NOTIFY_CHANGE_DIR_NAME, fname);
+ FILE_NOTIFY_CHANGE_DIR_NAME,
+ smb_dname->base_name);
}
/* Stat the fd for Samba bookkeeping. */
- if(SMB_VFS_FSTAT(fsp, psbuf) != 0) {
+ if(SMB_VFS_FSTAT(fsp, &smb_dname->st) != 0) {
fd_close(fsp);
file_free(req, fsp);
return map_nt_error_from_unix(errno);
}
/* Setup the files_struct for it. */
- fsp->mode = psbuf->st_mode;
- fsp->file_id = vfs_file_id_from_sbuf(conn, psbuf);
+ fsp->mode = smb_dname->st.st_ex_mode;
+ fsp->file_id = vfs_file_id_from_sbuf(conn, &smb_dname->st);
fsp->vuid = req ? req->vuid : UID_FIELD_INVALID;
fsp->file_pid = req ? req->smbpid : 0;
fsp->can_lock = False;
fsp->is_directory = True;
fsp->posix_open = posix_open;
- string_set(&fsp->fsp_name,fname);
+ string_set(&fsp->fsp_name, smb_dname->base_name);
- mtimespec = get_mtimespec(psbuf);
+ mtimespec = smb_dname->st.st_ex_mtime;
/*
* Still set the samba share mode lock for correct delete-on-close
* semantics and to make smbstatus more useful.
*/
lck = get_share_mode_lock(talloc_tos(), fsp->file_id,
- conn->connectpath,
- fname, &mtimespec);
+ conn->connectpath, smb_dname->base_name,
+ &mtimespec);
if (lck == NULL) {
DEBUG(0, ("onefs_open_directory: Could not get share mode "
- "lock for %s\n", fname));
+ "lock for %s\n", smb_fname_str_dbg(smb_dname)));
fd_close(fsp);
file_free(req, fsp);
return NT_STATUS_SHARING_VIOLATION;
return NT_STATUS_OK;
}
-/*
- * If a main file is opened for delete, all streams need to be checked for
- * !FILE_SHARE_DELETE. Do this by opening with DELETE_ACCESS.
- * If that works, delete them all by setting the delete on close and close.
- */
-
-static NTSTATUS open_streams_for_delete(connection_struct *conn,
- const char *fname)
-{
- struct stream_struct *stream_info;
- files_struct **streams;
- int i;
- unsigned int num_streams;
- TALLOC_CTX *frame = talloc_stackframe();
- NTSTATUS status;
-
- status = SMB_VFS_STREAMINFO(conn, NULL, fname, talloc_tos(),
- &num_streams, &stream_info);
-
- if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)
- || NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
- DEBUG(10, ("no streams around\n"));
- TALLOC_FREE(frame);
- return NT_STATUS_OK;
- }
-
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(10, ("SMB_VFS_STREAMINFO failed: %s\n",
- nt_errstr(status)));
- goto fail;
- }
-
- DEBUG(10, ("open_streams_for_delete found %d streams\n",
- num_streams));
-
- if (num_streams == 0) {
- TALLOC_FREE(frame);
- return NT_STATUS_OK;
- }
-
- streams = TALLOC_ARRAY(talloc_tos(), files_struct *, num_streams);
- if (streams == NULL) {
- DEBUG(0, ("talloc failed\n"));
- status = NT_STATUS_NO_MEMORY;
- goto fail;
- }
-
- for (i=0; i<num_streams; i++) {
- char *streamname;
-
- if (strequal(stream_info[i].name, "::$DATA")) {
- streams[i] = NULL;
- continue;
- }
-
- streamname = talloc_asprintf(talloc_tos(), "%s%s", fname,
- stream_info[i].name);
-
- if (streamname == NULL) {
- DEBUG(0, ("talloc_aprintf failed\n"));
- status = NT_STATUS_NO_MEMORY;
- goto fail;
- }
-
- status = onefs_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);
-
- if (!NT_STATUS_IS_OK(status)) {
- DEBUG(10, ("Could not open stream %s: %s\n",
- streamname, nt_errstr(status)));
- break;
- }
- }
-
- /*
- * don't touch the variable "status" beyond this point :-)
- */
-
- for (i -= 1 ; i >= 0; i--) {
- if (streams[i] == NULL) {
- continue;
- }
-
- DEBUG(10, ("Closing stream # %d, %s\n", i,
- streams[i]->fsp_name));
- close_file(NULL, streams[i], NORMAL_CLOSE);
- }
-
- fail:
- TALLOC_FREE(frame);
- return status;
-}
-
/*
* Wrapper around onefs_open_file_ntcreate and onefs_open_directory.
*/
static NTSTATUS onefs_create_file_unixpath(connection_struct *conn,
struct smb_request *req,
- const char *fname,
+ struct smb_filename *smb_fname,
uint32_t access_mask,
uint32_t share_access,
uint32_t create_disposition,
struct ea_list *ea_list,
files_struct **result,
int *pinfo,
- SMB_STRUCT_STAT *psbuf)
+ struct onefs_fsp_data *fsp_data)
{
- SMB_STRUCT_STAT sbuf;
int info = FILE_WAS_OPENED;
files_struct *base_fsp = NULL;
files_struct *fsp = NULL;
(unsigned int)create_disposition,
(unsigned int)create_options,
(unsigned int)oplock_request,
- ea_list, sd, fname));
+ ea_list, sd, smb_fname_str_dbg(smb_fname)));
if (create_options & FILE_OPEN_BY_FILE_ID) {
status = NT_STATUS_NOT_SUPPORTED;
}
if (req == NULL) {
+ SMB_ASSERT((oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK) ==
+ NO_OPLOCK);
oplock_request |= INTERNAL_OPEN_ONLY;
}
- if (psbuf != NULL) {
- sbuf = *psbuf;
- }
- else {
- if (SMB_VFS_STAT(conn, fname, &sbuf) == -1) {
- SET_STAT_INVALID(sbuf);
- }
+ if (lp_parm_bool(SNUM(conn), PARM_ONEFS_TYPE,
+ PARM_IGNORE_SACLS, PARM_IGNORE_SACLS_DEFAULT)) {
+ access_mask &= ~SYSTEM_SECURITY_ACCESS;
}
if ((conn->fs_capabilities & FILE_NAMED_STREAMS)
&& (access_mask & DELETE_ACCESS)
- && !is_ntfs_stream_name(fname)) {
+ && !is_ntfs_stream_smb_fname(smb_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 = open_streams_for_delete(conn, smb_fname->base_name);
if (!NT_STATUS_IS_OK(status)) {
goto fail;
}
if ((conn->fs_capabilities & FILE_NAMED_STREAMS)
- && is_ntfs_stream_name(fname)
- && (!(create_options & NTCREATEX_OPTIONS_PRIVATE_STREAM_DELETE))) {
- char *base;
+ && is_ntfs_stream_smb_fname(smb_fname)) {
uint32 base_create_disposition;
+ struct smb_filename *smb_fname_base = NULL;
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, ("onefs_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;
}
+ /* Create an smb_filename with stream_name == NULL. */
+ status = create_synthetic_smb_fname(talloc_tos(),
+ smb_fname->base_name,
+ NULL, NULL,
+ &smb_fname_base);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ if (SMB_VFS_STAT(conn, smb_fname_base) == -1) {
+ DEBUG(10, ("Unable to stat stream: %s\n",
+ smb_fname_str_dbg(smb_fname_base)));
+ }
+
status = onefs_create_file_unixpath(
conn, /* conn */
NULL, /* req */
- base, /* fname */
- 0, /* access_mask */
+ smb_fname_base, /* fname */
+ SYNCHRONIZE_ACCESS, /* access_mask */
(FILE_SHARE_READ |
FILE_SHARE_WRITE |
FILE_SHARE_DELETE), /* share_access */
base_create_disposition, /* create_disposition*/
0, /* create_options */
- 0, /* file_attributes */
+ file_attributes, /* file_attributes */
NO_OPLOCK, /* oplock_request */
0, /* allocation_size */
NULL, /* sd */
NULL, /* ea_list */
&base_fsp, /* result */
NULL, /* pinfo */
- NULL); /* psbuf */
+ NULL); /* fsp_data */
+
+ TALLOC_FREE(smb_fname_base);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(10, ("onefs_create_file_unixpath for base %s "
- "failed: %s\n", base, nt_errstr(status)));
+ "failed: %s\n", smb_fname->base_name,
+ nt_errstr(status)));
goto fail;
}
+
/*
- * we don't need to low level fd: This might conflict with
- * OneFS streams.
+ * Testing against windows xp/2003/vista shows that oplocks
+ * can actually be requested and granted on streams (see the
+ * RAW-OPLOCK-STREAM1 smbtorture test).
*/
- fd_close(base_fsp);
+ if ((oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK) !=
+ NO_OPLOCK) {
+ DEBUG(5, ("Oplock(%d) being requested on a stream! "
+ "Ignoring oplock request: fname=%s\n",
+ oplock_request & ~SAMBA_PRIVATE_OPLOCK_MASK,
+ smb_fname_str_dbg(smb_fname)));
+ /* Request NO_OPLOCK instead. */
+ oplock_request &= SAMBA_PRIVATE_OPLOCK_MASK;
+ }
}
/* Covert generic bits in the security descriptor. */
status = onefs_open_directory(
conn, /* conn */
req, /* req */
- fname, /* fname */
+ smb_fname, /* fname */
access_mask, /* access_mask */
share_access, /* share_access */
create_disposition, /* create_disposition*/
file_attributes, /* file_attributes */
sd, /* sd */
&fsp, /* result */
- &info, /* pinfo */
- &sbuf); /* psbuf */
+ &info); /* pinfo */
} else {
/*
status = onefs_open_file_ntcreate(
conn, /* conn */
req, /* req */
- fname, /* fname */
+ smb_fname, /* fname */
access_mask, /* access_mask */
share_access, /* share_access */
create_disposition, /* create_disposition*/
sd, /* sd */
fsp, /* result */
&info, /* pinfo */
- &sbuf); /* psbuf */
+ fsp_data); /* fsp_data */
if(!NT_STATUS_IS_OK(status)) {
file_free(req, fsp);
status = onefs_open_directory(
conn, /* conn */
req, /* req */
- fname, /* fname */
+ smb_fname, /* fname */
access_mask, /* access_mask */
share_access, /* share_access */
create_disposition, /* create_disposition*/
file_attributes, /* file_attributes */
sd, /* sd */
&fsp, /* result */
- &info, /* pinfo */
- &sbuf); /* psbuf */
+ &info); /* pinfo */
}
}
SMB_ASSERT(fsp);
if ((ea_list != NULL) && (info == FILE_WAS_CREATED)) {
- status = set_ea(conn, fsp, fname, ea_list);
+ status = set_ea(conn, fsp, smb_fname, ea_list);
if (!NT_STATUS_IS_OK(status)) {
goto fail;
}
}
- if (!fsp->is_directory && S_ISDIR(sbuf.st_mode)) {
+ if (!fsp->is_directory && S_ISDIR(smb_fname->st.st_ex_mode)) {
status = NT_STATUS_ACCESS_DENIED;
goto fail;
}
/* Save the requested allocation size. */
if ((info == FILE_WAS_CREATED) || (info == FILE_WAS_OVERWRITTEN)) {
if (allocation_size
- && (allocation_size > sbuf.st_size)) {
+ && (allocation_size > smb_fname->st.st_ex_size)) {
fsp->initial_allocation_size = smb_roundup(
fsp->conn, allocation_size);
if (fsp->is_directory) {
}
} else {
fsp->initial_allocation_size = smb_roundup(
- fsp->conn, (uint64_t)sbuf.st_size);
+ fsp->conn, (uint64_t)smb_fname->st.st_ex_size);
}
}
if (pinfo != NULL) {
*pinfo = info;
}
- if (psbuf != NULL) {
- if ((fsp->fh == NULL) || (fsp->fh->fd == -1)) {
- *psbuf = sbuf;
- }
- else {
- SMB_VFS_FSTAT(fsp, psbuf);
- }
+ if ((fsp->fh != NULL) && (fsp->fh->fd != -1)) {
+ SMB_VFS_FSTAT(fsp, &smb_fname->st);
}
return NT_STATUS_OK;
return status;
}
+static void destroy_onefs_fsp_data(void *p_data)
+{
+ struct onefs_fsp_data *fsp_data = (struct onefs_fsp_data *)p_data;
+
+ destroy_onefs_callback_record(fsp_data->oplock_callback_id);
+}
+
/**
* SMB_VFS_CREATE_FILE interface to onefs.
*/
NTSTATUS onefs_create_file(vfs_handle_struct *handle,
struct smb_request *req,
uint16_t root_dir_fid,
- const char *fname,
- uint32_t create_file_flags,
+ struct smb_filename *smb_fname,
uint32_t access_mask,
uint32_t share_access,
uint32_t create_disposition,
struct security_descriptor *sd,
struct ea_list *ea_list,
files_struct **result,
- int *pinfo,
- SMB_STRUCT_STAT *psbuf)
+ int *pinfo)
{
connection_struct *conn = handle->conn;
- struct case_semantics_state *case_state = NULL;
- SMB_STRUCT_STAT sbuf;
+ struct onefs_fsp_data fsp_data = {};
int info = FILE_WAS_OPENED;
files_struct *fsp = NULL;
NTSTATUS status;
"create_disposition = 0x%x create_options = 0x%x "
"oplock_request = 0x%x "
"root_dir_fid = 0x%x, ea_list = 0x%p, sd = 0x%p, "
- "create_file_flags = 0x%x, fname = %s\n",
+ "fname = %s\n",
(unsigned int)access_mask,
(unsigned int)file_attributes,
(unsigned int)share_access,
(unsigned int)create_options,
(unsigned int)oplock_request,
(unsigned int)root_dir_fid,
- ea_list, sd, create_file_flags, fname));
+ ea_list, sd, smb_fname_str_dbg(smb_fname)));
/* Get the file name if root_dir_fid was specified. */
if (root_dir_fid != 0) {
- char *new_fname;
-
status = get_relative_fid_filename(conn, req, root_dir_fid,
- fname, &new_fname);
+ smb_fname);
if (!NT_STATUS_IS_OK(status)) {
goto fail;
}
-
- fname = new_fname;
}
- /* Resolve the file name if this was a DFS pathname. */
- 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);
- }
-
- /* Convert dos path to unix path if it hasn't already been done. */
- if (create_file_flags & CFF_DOS_PATH) {
- 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;
- } else {
- if (psbuf != NULL) {
- sbuf = *psbuf;
- } else {
- if (SMB_VFS_STAT(conn, fname, &sbuf) == -1) {
- SET_STAT_INVALID(sbuf);
- }
- }
-
- }
-
- TALLOC_FREE(case_state);
-
/* All file access must go through check_name() */
- status = check_name(conn, fname);
+ status = check_name(conn, smb_fname->base_name);
if (!NT_STATUS_IS_OK(status)) {
goto fail;
}
status = onefs_create_file_unixpath(
conn, /* conn */
req, /* req */
- fname, /* fname */
+ smb_fname, /* fname */
access_mask, /* access_mask */
share_access, /* share_access */
create_disposition, /* create_disposition*/
ea_list, /* ea_list */
&fsp, /* result */
&info, /* pinfo */
- &sbuf); /* psbuf */
+ &fsp_data); /* psbuf */
if (!NT_STATUS_IS_OK(status)) {
goto fail;
DEBUG(10, ("onefs_create_file: info=%d\n", info));
+ /*
+ * Setup private onefs_fsp_data. Currently the private data struct is
+ * only used to store the oplock_callback_id so that when the file is
+ * closed, the onefs_callback_record can be properly cleaned up in the
+ * oplock_onefs sub-system.
+ */
+ if (fsp) {
+ struct onefs_fsp_data *fsp_data_tmp = NULL;
+ fsp_data_tmp = (struct onefs_fsp_data *)
+ VFS_ADD_FSP_EXTENSION(handle, fsp, struct onefs_fsp_data,
+ &destroy_onefs_fsp_data);
+
+ if (fsp_data_tmp == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+
+ *fsp_data_tmp = fsp_data;
+ }
+
*result = fsp;
if (pinfo != NULL) {
*pinfo = info;
}
- if (psbuf != NULL) {
- *psbuf = sbuf;
- }
return NT_STATUS_OK;
fail: