From b2e6a60710e7d71fa4cc930aee3024e68c4d9c9c Mon Sep 17 00:00:00 2001 From: Casey Bodley Date: Fri, 6 Apr 2012 16:15:24 -0400 Subject: [PATCH] ea: refactor SetEaInfo for use on open when open is given an ea buffer, pass it to new function nfs41_ea_set() after successful file creation. matches NTFS behavior on all dispositions: sets EAs on FILE_CREATE, FILE_OVERWRITE, FILE_OVERWRITE_IF, FILE_SUPERSEDE. does not set EAs on FILE_OPEN. only sets EAs on FILE_OPEN_IF if file did not previously exist. see new function create_with_ea() nfs41_ea_set() returns nfs error codes. uses NFS4ERR_FBIG when the EaValueLength exceeds NFS4_EASIZE (256). this gets mapped to windows error ERROR_FILE_TOO_LARGE, which the driver now converts to STATUS_EA_TOO_LARGE Signed-off-by: Casey Bodley --- daemon/ea.c | 206 +++++++++++++++++++++++++++------------------ daemon/nfs41.h | 7 ++ daemon/nfs41_ops.c | 3 + daemon/open.c | 17 ++++ sys/nfs41_driver.c | 1 + 5 files changed, 151 insertions(+), 83 deletions(-) diff --git a/daemon/ea.c b/daemon/ea.c index bfed99d..53cb3a0 100644 --- a/daemon/ea.c +++ b/daemon/ea.c @@ -30,6 +30,111 @@ #include "daemon_debug.h" +static int set_ea_value( + IN nfs41_session *session, + IN nfs41_path_fh *parent, + IN state_owner4 *owner, + IN PFILE_FULL_EA_INFORMATION ea) +{ + nfs41_path_fh file = { 0 }; + nfs41_file_info createattrs; + open_claim4 claim; + stateid_arg stateid; + open_delegation4 delegation = { 0 }; + nfs41_write_verf verf; + uint32_t bytes_written; + int status; + + /* don't allow values larger than NFS4_EASIZE */ + if (ea->EaValueLength > NFS4_EASIZE) { + eprintf("trying to write extended attribute value of size %d, " + "max allowed %d\n", ea->EaValueLength, NFS4_EASIZE); + status = NFS4ERR_FBIG; + goto out; + } + /* remove the file on empty value */ + if (ea->EaValueLength == 0) { + nfs41_component name; + name.name = ea->EaName; + name.len = ea->EaNameLength; + nfs41_remove(session, parent, &name, 0); + status = NFS4_OK; + goto out; + } + + claim.claim = CLAIM_NULL; + claim.u.null.filename = &file.name; + file.name.name = ea->EaName; + file.name.len = ea->EaNameLength; + + createattrs.attrmask.count = 2; + createattrs.attrmask.arr[0] = FATTR4_WORD0_SIZE; + createattrs.attrmask.arr[1] = FATTR4_WORD1_MODE; + createattrs.size = ea->EaValueLength; + createattrs.mode = 0664; + + status = nfs41_open(session, parent, &file, owner, &claim, + OPEN4_SHARE_ACCESS_WRITE | OPEN4_SHARE_ACCESS_WANT_NO_DELEG, + OPEN4_SHARE_DENY_BOTH, OPEN4_CREATE, UNCHECKED4, + &createattrs, TRUE, &stateid.stateid, &delegation, NULL); + if (status) { + dprintf(1, "nfs41_open() failed with %s\n", nfs_error_string(status)); + goto out; + } + + status = nfs41_write(session, &file, &stateid, + (unsigned char*)ea->EaName + ea->EaNameLength + 1, + ea->EaValueLength, 0, FILE_SYNC4, &bytes_written, + &verf, NULL); + if (status) { + eprintf("nfs41_write() failed with %s\n", nfs_error_string(status)); + goto out_close; + } + +out_close: + nfs41_close(session, &file, &stateid); +out: + return status; +} + +static int is_cygwin_ea( + PFILE_FULL_EA_INFORMATION ea) +{ + return (strncmp("NfsV3Attributes", ea->EaName, ea->EaNameLength) == 0 + && sizeof("NfsV3Attributes")-1 == ea->EaNameLength) + || (strncmp("NfsActOnLink", ea->EaName, ea->EaNameLength) == 0 + && sizeof("NfsActOnLink")-1 == ea->EaNameLength) + || (strncmp("NfsSymlinkTargetName", ea->EaName, ea->EaNameLength) == 0 + && sizeof("NfsSymlinkTargetName")-1 == ea->EaNameLength); +} + +int nfs41_ea_set( + IN nfs41_open_state *state, + IN PFILE_FULL_EA_INFORMATION ea) +{ + nfs41_path_fh attrdir = { 0 }; + int status; + + status = nfs41_rpc_openattr(state->session, &state->file, TRUE, &attrdir.fh); + if (status) { + eprintf("nfs41_rpc_openattr() failed with error %s\n", + nfs_error_string(status)); + goto out; + } + + while (status == NFS4_OK) { + if (!is_cygwin_ea(ea)) + status = set_ea_value(state->session, &attrdir, &state->owner, ea); + + if (ea->NextEntryOffset == 0) + break; + ea = (PFILE_FULL_EA_INFORMATION)((PBYTE)ea + ea->NextEntryOffset); + } +out: + return status; +} + + /* NFS41_EA_SET */ static int parse_setexattr(unsigned char *buffer, uint32_t length, nfs41_upcall *upcall) { @@ -54,104 +159,39 @@ static int handle_setexattr(nfs41_upcall *upcall) int status; setexattr_upcall_args *args = &upcall->args.setexattr; nfs41_open_state *state = upcall->state_ref; - stateid_arg stateid; - nfs41_file_info createattrs, info = { 0 }; - PFILE_FULL_EA_INFORMATION eainfo = - (PFILE_FULL_EA_INFORMATION)args->buf, prev = NULL; - nfs41_path_fh parent = { 0 }, file = { 0 }; - open_claim4 claim; - stateid4 open_stateid; - nfs41_write_verf verf; - uint32_t bytes_written; - UCHAR *buf; - open_delegation4 delegation = { 0 }; - - createattrs.attrmask.count = 2; - createattrs.attrmask.arr[0] = FATTR4_WORD0_SIZE; - createattrs.attrmask.arr[1] = FATTR4_WORD1_MODE; - createattrs.size = 0; - createattrs.mode = 0664; + PFILE_FULL_EA_INFORMATION ea = + (PFILE_FULL_EA_INFORMATION)args->buf; /* break read delegations before SETATTR */ nfs41_delegation_return(state->session, &state->file, OPEN_DELEGATE_READ, FALSE); - nfs41_open_stateid_arg(state, &stateid); - - if (strncmp("NfsV3Attributes", eainfo->EaName, eainfo->EaNameLength) == 0 - && sizeof("NfsV3Attributes")-1 == eainfo->EaNameLength) { + if (strncmp("NfsV3Attributes", ea->EaName, ea->EaNameLength) == 0 + && sizeof("NfsV3Attributes")-1 == ea->EaNameLength) { + nfs41_file_info info; + stateid_arg stateid; + + nfs41_open_stateid_arg(state, &stateid); + info.mode = args->mode; - info.attrmask.arr[1] |= FATTR4_WORD1_MODE; + info.attrmask.arr[0] = 0; + info.attrmask.arr[1] = FATTR4_WORD1_MODE; info.attrmask.count = 2; + status = nfs41_setattr(state->session, &state->file, &stateid, &info); if (status) { dprintf(1, "nfs41_setattr() failed with error %s.\n", nfs_error_string(status)); - return nfs_to_windows_error(status, ERROR_NOT_SUPPORTED); + goto out; } + args->ctime = info.change; - } else { - status = nfs41_rpc_openattr(state->session, &state->file, TRUE, &parent.fh); - if (status) { - dprintf(1, "handle_setexattr: nfs41_rpc_openattr() failed with error %s.\n", - nfs_error_string(status)); - return nfs_to_windows_error(status, ERROR_NOT_SUPPORTED); - } - - while (eainfo != prev) { - /* we don't allow for extended attribute values to be larger than NFS4_EASIZE. - * thus, let's not allow setting such. - */ - if (eainfo->EaValueLength > NFS4_EASIZE) { - dprintf(1, "trying to write extended attribute value of size %d" - "max allowed %d\n", eainfo->EaValueLength, NFS4_EASIZE); - status = ERROR_INVALID_DATA; - goto out; - } - file.name.name = eainfo->EaName; - file.name.len = eainfo->EaNameLength; - claim.claim = CLAIM_NULL; - claim.u.null.filename = &file.name; - status = nfs41_open(state->session, &parent, &file, &state->owner, &claim, - OPEN4_SHARE_ACCESS_WRITE, OPEN4_SHARE_DENY_BOTH, OPEN4_CREATE, - UNCHECKED4, &createattrs, TRUE, &open_stateid, &delegation, NULL); - if (status) { - dprintf(1, "handle_setexattr: nfs41_rpc_open() failed with error %s.\n", - nfs_error_string(status)); - status = nfs_to_windows_error(status, ERROR_NOT_SUPPORTED); - goto out; - } - - stateid.stateid = open_stateid; - stateid.stateid.seqid = 0; - buf = (UCHAR *) eainfo->EaName + eainfo->EaNameLength + 1; - status = nfs41_write(state->session, &file, &stateid, buf, - eainfo->EaValueLength, 0, FILE_SYNC4, &bytes_written, - &verf, NULL); - if (status) { - dprintf(1, "handle_setexattr: nfs41_write() failed w/error %s.\n", - nfs_error_string(status)); - status = nfs_to_windows_error(status, ERROR_NOT_SUPPORTED); - nfs41_close(state->session, &file, &stateid); - goto out; - } - - status = nfs41_close(state->session, &file, &stateid); - if (status) { - dprintf(1, "handle_setexattr: nfs41_close() failed w/error %s.\n", - nfs_error_string(status)); - status = nfs_to_windows_error(status, ERROR_NOT_SUPPORTED); - goto out; - } - - bytes_written = 0; - prev = eainfo; - eainfo = (FILE_FULL_EA_INFORMATION *) ((ULONG_PTR) eainfo + - eainfo->NextEntryOffset); - } + goto out; } + + status = nfs41_ea_set(state, ea); out: - return status; + return nfs_to_windows_error(status, ERROR_NOT_SUPPORTED); } static int marshall_setexattr(unsigned char *buffer, uint32_t *length, nfs41_upcall *upcall) diff --git a/daemon/nfs41.h b/daemon/nfs41.h index 7aa6c18..d9478fe 100644 --- a/daemon/nfs41.h +++ b/daemon/nfs41.h @@ -503,4 +503,11 @@ void nfs41_open_stateid_arg( IN nfs41_open_state *state, OUT struct __stateid_arg *arg); + +/* ea.c */ +struct _FILE_FULL_EA_INFORMATION; +int nfs41_ea_set( + IN nfs41_open_state *state, + IN struct _FILE_FULL_EA_INFORMATION *ea); + #endif /* __NFS41__ */ diff --git a/daemon/nfs41_ops.c b/daemon/nfs41_ops.c index 30fdd94..fce831b 100644 --- a/daemon/nfs41_ops.c +++ b/daemon/nfs41_ops.c @@ -1250,6 +1250,9 @@ int nfs41_remove( if (compound_error(status = compound.res.status)) goto out; + if (info.type == NF4ATTRDIR) + goto out; + /* update the attributes of the parent directory */ memcpy(&info.attrmask, &getattr_res.obj_attributes.attrmask, sizeof(bitmap4)); diff --git a/daemon/open.c b/daemon/open.c index 22634ce..57d6573 100644 --- a/daemon/open.c +++ b/daemon/open.c @@ -442,6 +442,16 @@ static int check_execute_access(nfs41_open_state *state) return status; } +static int create_with_ea( + IN uint32_t disposition, + IN uint32_t lookup_status) +{ + /* only set EAs on file creation */ + return disposition == FILE_SUPERSEDE || disposition == FILE_CREATE + || disposition == FILE_OVERWRITE || disposition == FILE_OVERWRITE_IF + || (disposition == FILE_OPEN_IF && lookup_status == NFS4ERR_NOENT); +} + static int handle_open(nfs41_upcall *upcall) { int status = 0; @@ -665,7 +675,14 @@ supersede_retry: args->mode = info.mode; args->changeattr = info.change; } + + /* set extended attributes on file creation */ + if (args->ea && create_with_ea(args->disposition, lookup_status)) { + status = nfs41_ea_set(state, args->ea); + status = nfs_to_windows_error(status, ERROR_FILE_NOT_FOUND); + } } + upcall->state_ref = state; nfs41_open_state_ref(upcall->state_ref); out: diff --git a/sys/nfs41_driver.c b/sys/nfs41_driver.c index 1e352f3..498b1ee 100644 --- a/sys/nfs41_driver.c +++ b/sys/nfs41_driver.c @@ -4524,6 +4524,7 @@ NTSTATUS map_setea_error( case ERROR_NOT_SUPPORTED: return STATUS_NOT_IMPLEMENTED; case ERROR_NETWORK_ACCESS_DENIED: return STATUS_NETWORK_ACCESS_DENIED; case ERROR_NETNAME_DELETED: return STATUS_NETWORK_NAME_DELETED; + case ERROR_FILE_TOO_LARGE: return STATUS_EA_TOO_LARGE; case ERROR_BUFFER_OVERFLOW: return STATUS_INSUFFICIENT_RESOURCES; default: print_error("failed to map windows error %d to NTSTATUS; "