- diff --git a/cygwin/devel/msnfs41client.bash b/cygwin/devel/msnfs41client.bash
- index c963e45..7152bfc 100755
- --- a/cygwin/devel/msnfs41client.bash
- +++ b/cygwin/devel/msnfs41client.bash
- @@ -690,7 +690,7 @@ function nfsclient_rundeamon
- "${nfsd_args[@]}"
- )
- "${nfsd_args[@]}"
- - elif false ; then
- + elif true ; then
- export _NT_ALT_SYMBOL_PATH="$(cygpath -w "$PWD");srv*https://msdl.microsoft.com/download/symbols"
- #
- # Useful cdb cmds:
- @@ -830,7 +830,7 @@ function nfsclient_system_rundeamon
- )
- "${nfsd_args[@]}"
- - elif false ; then
- + elif true ; then
- export _NT_ALT_SYMBOL_PATH="$(cygpath -w "${sbinpath}");srv*https://msdl.microsoft.com/download/symbols"
- # - heap tests (eats lots of memory):
- # '-c' '!gflag +full;g' for heap tests
- diff --git a/daemon/fileinfoutil.c b/daemon/fileinfoutil.c
- index 0ed40c8..653f2bb 100644
- --- a/daemon/fileinfoutil.c
- +++ b/daemon/fileinfoutil.c
- @@ -59,7 +59,7 @@ ULONG nfs_file_info_to_attributes(
- if (info->symlink_dir)
- attrs |= FILE_ATTRIBUTE_DIRECTORY;
- }
- - else if (info->type == NF4REG) {
- + else if (info->type == NF4REG || info->type == NF4NAMEDATTR) {
- if (superblock->sparse_file_support) {
- /* FIXME: What about pNFS ? */
- attrs |= FILE_ATTRIBUTE_SPARSE_FILE;
- @@ -383,6 +383,7 @@ void nfs_to_stat_lx_info(
- stat_lx_out->LxMode = 0UL;
- switch(info->type) {
- case NF4REG:
- + case NF4NAMEDATTR:
- stat_lx_out->LxMode |= LX_MODE_S_IFREG;
- break;
- case NF4DIR:
- diff --git a/daemon/getattr.c b/daemon/getattr.c
- index 4744285..a100470 100644
- --- a/daemon/getattr.c
- +++ b/daemon/getattr.c
- @@ -204,6 +204,9 @@ static int handle_getattr(void *daemon_context, nfs41_upcall *upcall)
- &args->stat_lx_info);
- break;
- #endif /* NFS41_DRIVER_WSL_SUPPORT */
- +#ifdef NFS41_STREAM_SUPPORT
- + case FileStreamInformation:
- +#endif /* NFS41_STREAM_SUPPORT */
- default:
- eprintf("handle_getattr(state->path.path='%s'): "
- "unhandled file query class %d\n",
- diff --git a/daemon/lookup.c b/daemon/lookup.c
- index 3e9e591..48e4c1c 100644
- --- a/daemon/lookup.c
- +++ b/daemon/lookup.c
- @@ -502,6 +502,146 @@ int nfs41_lookup(
- DPRINTF(LULVL, ("--> nfs41_lookup('%s')\n", path.path));
- +#ifdef NFS41_STREAM_SUPPORT
- + /* Fast check for potential stream: Look for a colon */
- + if (memchr(path_inout->path, ':', path_inout->len)) {
- + char base_name[NFS41_MAX_PATH_LEN+1];
- + char stream_name[NFS41_MAX_COMPONENT_LEN+1];
- +
- + /* Parse the stream syntax */
- + if (parse_win32stream_name(path.path, base_name, stream_name)) {
- + if (stream_name[0] == '\0') {
- + /* "foo:$DATA" case -> treat as "foo" */
- + DPRINTF(0, ("nfs41_lookup: 'foo:$DATA' case -> treat as 'foo'\n"));
- + size_t base_len = strlen(base_name);
- +
- + (void)memcpy(path.path, base_name, base_len);
- + path.path[base_len] = '\0';
- + path.len = (unsigned short)base_len;
- + path_pos = path.path;
- + path_end = path.path + path.len;
- +
- + /* Continue to normal lookup with stripped name */
- + } else {
- + /* "foo:bar" case */
- +
- + /* 1. Recursively lookup the base file */
- + nfs41_abs_path base_path = path;
- + size_t base_len = strlen(base_name);
- +
- + (void)memcpy(base_path.path, base_name, base_len);
- + base_path.path[base_len] = '\0';
- + base_path.len = (unsigned short)base_len;
- +
- + nfs41_path_fh attr_target;
- + if (target_out == NULL) {
- + target_out = &attr_target;
- + }
- +
- + status = nfs41_lookup(root, session, casesensitive, &base_path,
- + parent_out, target_out, info_out, session_out);
- +
- + if (status == 0) {
- + /*
- + * Use the session returned by the base lookup if applicable
- + */
- + nfs41_session *lookup_session;
- + nfs41_compound compound;
- + nfs_argop4 argops[8];
- + nfs_resop4 resops[8];
- + nfs41_sequence_args sequence_args;
- + nfs41_sequence_res sequence_res;
- + nfs41_openattr_args openattr_args;
- + nfs41_openattr_res openattr_res;
- + nfs41_putfh_args putfh_args = { 0 };
- + nfs41_putfh_res putfh_res;
- + nfs41_lookup_args lookup_args = { 0 };
- + nfs41_lookup_res lookup_res;
- + nfs41_getfh_res getfh_res;
- + nfs41_getattr_args getattr_args = { 0 };
- + nfs41_getattr_res getattr_res = { 0 };
- + nfs41_component stream_comp = {
- + .name = stream_name,
- + .len = (unsigned short)strlen(stream_name)
- + };
- + /* Minimal attributes for stream */
- + bitmap4 attr_request = {
- + .count = 2,
- + .arr = {
- + [0] = FATTR4_WORD0_TYPE | FATTR4_WORD0_CHANGE |
- + FATTR4_WORD0_SIZE | FATTR4_WORD0_FSID |
- + FATTR4_WORD0_FILEID,
- + [1] = FATTR4_WORD1_MODE | FATTR4_WORD1_SPACE_USED |
- + FATTR4_WORD1_TIME_ACCESS |
- + FATTR4_WORD1_TIME_CREATE |
- + FATTR4_WORD1_TIME_MODIFY |
- + FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP,
- + [2] = 0
- + }
- + };
- +
- + DPRINTF(1,
- + ("nfs41_lookup: "
- + "Looking up base_name='%s'/stream_name='%s'\n",
- + base_name, stream_name));
- +
- + if ((session_out != NULL) && (*session_out != NULL))
- + lookup_session = *session_out;
- + else
- + lookup_session = session;
- +
- + compound_init(&compound,
- + lookup_session->client->root->nfsminorvers,
- + argops, resops, "lookup_win32stream");
- +
- + compound_add_op(&compound, OP_SEQUENCE, &sequence_args, &sequence_res);
- + nfs41_session_sequence(&sequence_args, lookup_session, 0);
- +
- + compound_add_op(&compound, OP_PUTFH, &putfh_args, &putfh_res);
- + putfh_args.file = target_out;
- + putfh_args.in_recovery = FALSE;
- +
- + compound_add_op(&compound, OP_OPENATTR, &openattr_args, &openattr_res);
- + openattr_args.createdir = FALSE;
- +
- + compound_add_op(&compound, OP_LOOKUP, &lookup_args, &lookup_res);
- + lookup_args.name = &stream_comp;
- +
- + compound_add_op(&compound, OP_GETFH, NULL, &getfh_res);
- + getfh_res.fh = &target_out->fh;
- +
- + compound_add_op(&compound, OP_GETATTR, &getattr_args, &getattr_res);
- + getattr_args.attr_request = &attr_request;
- + /* If caller wanted info, fill it; else dummy */
- + nfs41_file_info dummy_info;
- + if (info_out)
- + getattr_res.info = info_out;
- + else
- + getattr_res.info = &dummy_info;
- + getattr_res.obj_attributes.attr_vals_len = NFS4_OPAQUE_LIMIT_ATTR;
- +
- + status = compound_encode_send_decode(lookup_session, &compound, TRUE);
- +
- + if (status == 0)
- + status = compound.res.status;
- +
- + if (status == 0) {
- + /* We MUST return a |NF4NAMEDATTR|, and never a |NF4ATTRDIR| */
- + EASSERT(getattr_res.info->type != NF4ATTRDIR);
- + EASSERT(getattr_res.info->type == NF4NAMEDATTR);
- + }
- + else {
- + DPRINTF(1, ("nfs41_lookup: failed for attr, status=%d\n",
- + (int)status));
- + }
- + }
- +
- + goto out;
- + }
- + }
- + }
- +#endif /* NFS41_STREAM_SUPPORT */
- +
- if (parent_out == NULL) parent_out = &parent;
- if (target_out == NULL) target_out = ⌖
- parent_out->fh.len = target_out->fh.len = 0;
- diff --git a/daemon/nfs41_ops.c b/daemon/nfs41_ops.c
- index 9012db4..a0c1e6e 100644
- --- a/daemon/nfs41_ops.c
- +++ b/daemon/nfs41_ops.c
- @@ -453,8 +453,8 @@ int nfs41_open(
- {
- int status;
- nfs41_compound compound;
- - nfs_argop4 argops[8];
- - nfs_resop4 resops[8];
- + nfs_argop4 argops[12];
- + nfs_resop4 resops[12];
- nfs41_sequence_args sequence_args;
- nfs41_sequence_res sequence_res;
- nfs41_putfh_args putfh_args[2];
- @@ -471,10 +471,41 @@ int nfs41_open(
- bool_t current_fh_is_dir;
- bool_t already_delegated = delegation->type == OPEN_DELEGATE_READ
- || delegation->type == OPEN_DELEGATE_WRITE;
- +#ifdef NFS41_STREAM_SUPPORT
- + char base_buf[NFS41_MAX_COMPONENT_LEN];
- + char stream_buf[NFS41_MAX_COMPONENT_LEN];
- + bool is_stream = false;
- + nfs41_component base_comp = {0};
- + nfs41_component stream_comp = {0};
- + nfs41_lookup_args lookup_args;
- + nfs41_lookup_res lookup_res;
- + nfs41_openattr_args openattr_args;
- + nfs41_openattr_res openattr_res;
- +#endif /* NFS41_STREAM_SUPPORT */
- EASSERT_IS_VALID_NON_NULL_PTR(parent);
- EASSERT_IS_VALID_NON_NULL_PTR(parent->fh.superblock);
- +#ifdef NFS41_STREAM_SUPPORT
- + if (file->name.name) {
- + if ((memchr(file->name.name, ':', file->name.len) != NULL)) {
- + if (parse_win32stream_name(file->name.name,
- + base_buf, stream_buf)) {
- + if (stream_buf[0] != '\0') {
- + is_stream = true;
- + base_comp.name = base_buf;
- + base_comp.len = (unsigned short)strlen(base_buf);
- + stream_comp.name = stream_buf;
- + stream_comp.len = (unsigned short)strlen(stream_buf);
- + }
- + } else {
- + status = ERROR_INVALID_NAME;
- + goto out;
- + }
- + }
- + }
- +#endif /* NFS41_STREAM_SUPPORT */
- +
- /* depending on the claim type, OPEN expects CURRENT_FH set
- * to either the parent directory, or to the file itself */
- switch (claim->claim) {
- @@ -524,6 +555,16 @@ int nfs41_open(
- putfh_args[0].in_recovery = 0;
- }
- +#ifdef NFS41_STREAM_SUPPORT
- + if (is_stream) {
- + compound_add_op(&compound, OP_LOOKUP, &lookup_args, &lookup_res);
- + lookup_args.name = &base_comp;
- + compound_add_op(&compound, OP_OPENATTR, &openattr_args, &openattr_res);
- + /* If we are creating the stream, we should allow creating the attr dir */
- + openattr_args.createdir = (create == OPEN4_CREATE) ? TRUE : FALSE;
- + }
- +#endif /* NFS41_STREAM_SUPPORT */
- +
- compound_add_op(&compound, OP_OPEN, &open_args, &open_res);
- open_args.seqid = 0;
- #ifdef DISABLE_FILE_DELEGATIONS
- @@ -549,6 +590,18 @@ int nfs41_open(
- parent->fh.superblock, &createattrs->attrmask);
- }
- open_args.claim = claim;
- +
- +#ifdef NFS41_STREAM_SUPPORT
- + if (is_stream &&
- + ((claim->claim == CLAIM_NULL) ||
- + (claim->claim == CLAIM_DELEGATE_CUR))) {
- + if (claim->claim == CLAIM_NULL)
- + claim->u.null.filename = &stream_comp;
- + else if (claim->claim == CLAIM_DELEGATE_CUR)
- + claim->u.deleg_cur.name = &stream_comp;
- + }
- +#endif /* NFS41_STREAM_SUPPORT */
- +
- open_res.resok4.stateid = stateid;
- open_res.resok4.delegation = delegation;
- @@ -582,6 +635,7 @@ int nfs41_open(
- if (compound_error(status = compound.res.status))
- goto out;
- + /* This can happen if |nfs41_open()| is called by the EA code */
- if (dir_info.type == NF4ATTRDIR) {
- file->fh.superblock = parent->fh.superblock;
- goto out;
- @@ -614,8 +668,8 @@ int nfs41_create(
- {
- int status;
- nfs41_compound compound;
- - nfs_argop4 argops[8];
- - nfs_resop4 resops[8];
- + nfs_argop4 argops[12];
- + nfs_resop4 resops[12];
- nfs41_sequence_args sequence_args;
- nfs41_sequence_res sequence_res;
- nfs41_putfh_args putfh_args;
- @@ -629,6 +683,37 @@ int nfs41_create(
- nfs41_file_info dir_info;
- nfs41_savefh_res savefh_res;
- nfs41_restorefh_res restorefh_res;
- +#ifdef NFS41_STREAM_SUPPORT
- + char base_buf[NFS41_MAX_COMPONENT_LEN];
- + char stream_buf[NFS41_MAX_COMPONENT_LEN];
- + bool is_stream = false;
- + nfs41_component base_comp = {0};
- + nfs41_component stream_comp = {0};
- + nfs41_lookup_args lookup_args;
- + nfs41_lookup_res lookup_res;
- + nfs41_openattr_args openattr_args;
- + nfs41_openattr_res openattr_res;
- +#endif /* NFS41_STREAM_SUPPORT */
- +
- +#ifdef NFS41_STREAM_SUPPORT
- + if (file->name.name) {
- + if ((memchr(file->name.name, ':', file->name.len) != NULL)) {
- + if (parse_win32stream_name(file->name.name,
- + base_buf, stream_buf)) {
- + if (stream_buf[0] != '\0') {
- + is_stream = true;
- + base_comp.name = base_buf;
- + base_comp.len = (unsigned short)strlen(base_buf);
- + stream_comp.name = stream_buf;
- + stream_comp.len = (unsigned short)strlen(stream_buf);
- + }
- + } else {
- + status = ERROR_INVALID_NAME;
- + goto out;
- + }
- + }
- + }
- +#endif /* NFS41_STREAM_SUPPORT */
- nfs41_superblock_getattr_mask(parent->fh.superblock, &attr_request);
- @@ -644,13 +729,31 @@ int nfs41_create(
- compound_add_op(&compound, OP_SAVEFH, NULL, &savefh_res);
- +#ifdef NFS41_STREAM_SUPPORT
- + if (is_stream) {
- + compound_add_op(&compound, OP_LOOKUP, &lookup_args, &lookup_res);
- + lookup_args.name = &base_comp;
- + compound_add_op(&compound, OP_OPENATTR, &openattr_args, &openattr_res);
- + openattr_args.createdir = TRUE;
- + }
- +#endif /* NFS41_STREAM_SUPPORT */
- +
- compound_add_op(&compound, OP_CREATE, &create_args, &create_res);
- create_args.objtype.type = type;
- if (type == NF4LNK) {
- create_args.objtype.u.lnk.linkdata = symlink;
- create_args.objtype.u.lnk.linkdata_len = (uint32_t)strlen(symlink);
- }
- - create_args.name = &file->name;
- +
- +#ifdef NFS41_STREAM_SUPPORT
- + if (is_stream) {
- + create_args.name = &stream_comp;
- + }
- + else
- +#endif /* NFS41_STREAM_SUPPORT */
- + {
- + create_args.name = &file->name;
- + }
- create_args.createattrs = createattrs;
- nfs41_superblock_supported_attrs(
- parent->fh.superblock, &createattrs->attrmask);
- @@ -1283,8 +1386,8 @@ int nfs41_remove(
- {
- int status;
- nfs41_compound compound;
- - nfs_argop4 argops[4];
- - nfs_resop4 resops[4];
- + nfs_argop4 argops[8];
- + nfs_resop4 resops[8];
- nfs41_sequence_args sequence_args;
- nfs41_sequence_res sequence_res;
- nfs41_putfh_args putfh_args;
- @@ -1295,6 +1398,35 @@ int nfs41_remove(
- nfs41_getattr_res getattr_res NDSH(= { 0 });
- bitmap4 attr_request;
- nfs41_file_info info;
- +#ifdef NFS41_STREAM_SUPPORT
- + char base_buf[NFS41_MAX_COMPONENT_LEN];
- + char stream_buf[NFS41_MAX_COMPONENT_LEN];
- + bool is_stream = false;
- + nfs41_component base_comp = {0};
- + nfs41_component stream_comp = {0};
- + nfs41_lookup_args lookup_args;
- + nfs41_lookup_res lookup_res;
- + nfs41_openattr_args openattr_args;
- + nfs41_openattr_res openattr_res;
- +
- + if (target->name) {
- + if ((memchr(target->name, ':', target->len) != NULL)) {
- + if (parse_win32stream_name(target->name,
- + base_buf, stream_buf)) {
- + if (stream_buf[0] != '\0') {
- + is_stream = true;
- + base_comp.name = base_buf;
- + base_comp.len = (unsigned short)strlen(base_buf);
- + stream_comp.name = stream_buf;
- + stream_comp.len = (unsigned short)strlen(stream_buf);
- + }
- + } else {
- + status = ERROR_INVALID_NAME;
- + goto out;
- + }
- + }
- + }
- +#endif /* NFS41_STREAM_SUPPORT */
- nfs41_superblock_getattr_mask(parent->fh.superblock, &attr_request);
- @@ -1308,8 +1440,25 @@ int nfs41_remove(
- putfh_args.file = parent;
- putfh_args.in_recovery = 0;
- +#ifdef NFS41_STREAM_SUPPORT
- + if (is_stream) {
- + compound_add_op(&compound, OP_LOOKUP, &lookup_args, &lookup_res);
- + lookup_args.name = &base_comp;
- + compound_add_op(&compound, OP_OPENATTR, &openattr_args, &openattr_res);
- + openattr_args.createdir = FALSE;
- + }
- +#endif /* NFS41_STREAM_SUPPORT */
- +
- compound_add_op(&compound, OP_REMOVE, &remove_args, &remove_res);
- - remove_args.target = target;
- +#ifdef NFS41_STREAM_SUPPORT
- + if (is_stream) {
- + remove_args.target = &stream_comp;
- + }
- + else
- +#endif /* NFS41_STREAM_SUPPORT */
- + {
- + remove_args.target = target;
- + }
- compound_add_op(&compound, OP_GETATTR, &getattr_args, &getattr_res);
- getattr_args.attr_request = &attr_request;
- diff --git a/daemon/nfs41_superblock.c b/daemon/nfs41_superblock.c
- index 5ffb171..75a54a6 100644
- --- a/daemon/nfs41_superblock.c
- +++ b/daemon/nfs41_superblock.c
- @@ -303,8 +303,12 @@ void nfs41_superblock_fs_attributes(
- FsAttrs->FileSystemAttributes |= FILE_SUPPORTS_HARD_LINKS;
- if (superblock->symlink_support)
- FsAttrs->FileSystemAttributes |= FILE_SUPPORTS_REPARSE_POINTS;
- - if (superblock->ea_support)
- + if (superblock->ea_support) {
- FsAttrs->FileSystemAttributes |= FILE_SUPPORTS_EXTENDED_ATTRIBUTES;
- +#ifdef NFS41_STREAM_SUPPORT
- + FsAttrs->FileSystemAttributes |= FILE_NAMED_STREAMS;
- +#endif /* NFS41_STREAM_SUPPORT */
- + }
- if (superblock->case_preserving)
- FsAttrs->FileSystemAttributes |= FILE_CASE_PRESERVED_NAMES;
- if (!superblock->case_insensitive)
- diff --git a/daemon/open.c b/daemon/open.c
- index b5e72c7..4928bf7 100644
- --- a/daemon/open.c
- +++ b/daemon/open.c
- @@ -880,8 +880,12 @@ static int handle_open(void *daemon_context, nfs41_upcall *upcall)
- // first check if windows told us it's a directory
- if (args->create_opts & FILE_DIRECTORY_FILE)
- state->type = NF4DIR;
- - else
- - state->type = NF4REG;
- + else {
- + if (strstr(args->path, ":"))
- + state->type = NF4NAMEDATTR;
- + else
- + state->type = NF4REG;
- + }
- #ifdef NFS41_DRIVER_ALLOW_CREATEFILE_ACLS
- if (args->sec_desc_len) {
- @@ -1007,20 +1011,26 @@ static int handle_open(void *daemon_context, nfs41_upcall *upcall)
- goto out_free_state;
- }
- }
- - } else if (info.type == NF4REG) {
- + } else if (info.type == NF4REG || info.type == NF4NAMEDATTR) {
- DPRINTF(2, ("handle nfs41_open: FILE\n"));
- if (args->create_opts & FILE_DIRECTORY_FILE) {
- - DPRINTF(1, ("trying to open file '%s' as a directory\n",
- - state->path.path));
- + DPRINTF(1,
- + ("trying to open file state->path.path='%s',"
- + "info.type=%d as a directory\n",
- + state->path.path, (int)info.type));
- /*
- * Notes:
- * - SMB returns |STATUS_OBJECT_TYPE_MISMATCH|
- * while NTFS returns |STATUS_NOT_A_DIRECTORY|
- * - See |map_open_errors()| for the mapping to
- * |STATUS_*|
- + * - We do not return |ERROR_DIRECTORY| when opening a Win32
- + * stream
- */
- - status = ERROR_DIRECTORY;
- - goto out_free_state;
- + if (memchr(state->path.path, ':', state->path.len) == NULL) {
- + status = ERROR_DIRECTORY;
- + goto out_free_state;
- + }
- }
- } else if (info.type == NF4LNK) {
- DPRINTF(2, ("handle nfs41_open: SYMLINK\n"));
- @@ -1324,7 +1334,7 @@ supersede_retry:
- #ifdef DEBUG_OPEN_SPARSE_FILES
- if ((status == 0) &&
- - (info.type == NF4REG) &&
- + (info.type == NF4REG | info.type == NF4NAMEDATTR) &&
- (state->session->client->root->supports_nfs42_seek)) {
- //debug_list_sparsefile_holes(state);
- debug_list_sparsefile_datasections(state);
- @@ -1524,7 +1534,7 @@ static int handle_close(void *deamon_context, nfs41_upcall *upcall)
- nfs41_open_state *state = upcall->state_ref;
- /* return associated file layouts if necessary */
- - if (state->type == NF4REG)
- + if (state->type == NF4REG || state->type == NF4NAMEDATTR)
- pnfs_layout_state_close(state->session, state, args->remove);
- if (state->srv_open == args->srv_open)
- diff --git a/daemon/util.c b/daemon/util.c
- index 8d7f950..10bf3cc 100644
- --- a/daemon/util.c
- +++ b/daemon/util.c
- @@ -922,3 +922,85 @@ create_symlink_chgrp_out:
- return chgrp_status;
- }
- #endif /* NFS41_DRIVER_SETGID_NEWGRP_SUPPORT */
- +
- +#ifdef NFS41_STREAM_SUPPORT
- +/*
- + * Helper to parse Win32 stream names.
- + * Returns |true| if a stream syntax is detected, |false| otherwise.
- + * On success:
- + * - |base_name|: set to the base file name
- + * - |stream_name|: set to the stream name, or |stream_name[0]=='\0'|
- + * if it was the default stream (:$DATA).
- + *
- + * - FIXME: We should implement support for stream types, e.g.
- + * "filename:streamname:type", e.g. "foo:bar:$DATA", "foo::$DATA"
- + * (for the default stream)
- + */
- +bool parse_win32stream_name(
- + IN const char *restrict path,
- + OUT char *restrict base_name,
- + OUT char *restrict stream_name)
- +{
- + const char *colon = strrchr(path, ':');
- + const char *backslash = strrchr(path, '\\');
- +#if 1
- + const char *data_suffix = ":$DATA";
- + size_t path_len = strlen(path);
- +#endif
- + size_t base_len;
- +
- + /* Win32 streams are only allowed on the leaf component of a path */
- + if (colon && backslash && (colon < backslash))
- + return false;
- +
- + if (!colon)
- + return false;
- +
- +#if 1
- + /* Handle "foo:$DATA" case --> maps to "foo" */
- + if ((path_len >= 6) &&
- + _stricmp(path + path_len - 6, data_suffix) == 0) {
- + /* Check if there is another colon before this suffix */
- + const char *prev_colon = NULL;
- + const char *p = path;
- + while (p < path + path_len - 6) {
- + if (*p == ':')
- + prev_colon = p;
- + p++;
- + }
- +
- + if (prev_colon && ((backslash == NULL) || (prev_colon > backslash))) {
- + /* "foo:bar:$DATA" --> maps to stream "bar" on "foo" */
- + base_len = prev_colon - path;
- +
- + (void)memcpy(base_name, path, base_len);
- + base_name[base_len] = '\0';
- +
- + size_t stream_len = (path + path_len - 6) - (prev_colon + 1);
- + (void)memcpy(stream_name, prev_colon + 1, stream_len);
- + stream_name[stream_len] = '\0';
- +
- + DPRINTF(1,
- + ("parse_win32stream_name: "
- + "type case: base_name='%s'/stream_name='%s'\n",
- + base_name, stream_name));
- + return true;
- + }
- + }
- +#endif
- +
- + /* Standard case: "foo:bar" */
- + base_len = colon - path;
- + (void)memcpy(base_name, path, base_len);
- + base_name[base_len] = '\0';
- +
- + (void)strcpy(stream_name, colon + 1);
- +
- + DPRINTF(1,
- + ("parse_win32stream_name: "
- + "standard case: base_name='%s'/stream_name='%s'\n",
- + base_name, stream_name));
- +
- + return true;
- +}
- +#endif /* NFS41_STREAM_SUPPORT */
- diff --git a/daemon/util.h b/daemon/util.h
- index dc0ea07..a900a96 100644
- --- a/daemon/util.h
- +++ b/daemon/util.h
- @@ -458,4 +458,11 @@ int chgrp_to_primarygroup(
- IN nfs41_open_state *state);
- #endif /* NFS41_DRIVER_SETGID_NEWGRP_SUPPORT */
- +#ifdef NFS41_STREAM_SUPPORT
- +bool parse_win32stream_name(
- + IN const char *restrict path,
- + OUT char *restrict base_name,
- + OUT char *restrict stream_name);
- +#endif /* NFS41_STREAM_SUPPORT */
- +
- #endif /* !__NFS41_DAEMON_UTIL_H__ */
- diff --git a/nfs41_build_features.h b/nfs41_build_features.h
- index fb744f5..4bcc154 100644
- --- a/nfs41_build_features.h
- +++ b/nfs41_build_features.h
- @@ -273,4 +273,10 @@
- */
- #define NFS41_DRIVER_ALLOW_CREATEFILE_ACLS 1
- +/*
- + * |NFS41_STREAM_SUPPORT| - Enable Win32 named streams support using
- + * NFSv4.1 named attributes
- + */
- +#define NFS41_STREAM_SUPPORT 1
- +
- #endif /* !_NFS41_DRIVER_BUILDFEATURES_ */
- diff --git a/sys/nfs41sys_fileinfo.c b/sys/nfs41sys_fileinfo.c
- index f871915..1647836 100644
- --- a/sys/nfs41sys_fileinfo.c
- +++ b/sys/nfs41sys_fileinfo.c
- @@ -398,6 +398,9 @@ NTSTATUS nfs41_QueryFileInformation(
- case FileStatInformation:
- case FileStatLxInformation:
- #endif /* NFS41_DRIVER_WSL_SUPPORT */
- +#ifdef NFS41_STREAM_SUPPORT
- + case FileStreamInformation:
- +#endif /* NFS41_STREAM_SUPPORT */
- break;
- default:
- print_error("nfs41_QueryFileInformation: "
- @@ -500,6 +503,9 @@ NTSTATUS nfs41_QueryFileInformation(
- case FileStatInformation:
- case FileStatLxInformation:
- #endif /* NFS41_DRIVER_WSL_SUPPORT */
- +#ifdef NFS41_STREAM_SUPPORT
- + case FileStreamInformation:
- +#endif /* NFS41_STREAM_SUPPORT */
- break;
- default:
- print_error("nfs41_QueryFileInformation: "
- diff --git a/sys/nfs41sys_openclose.c b/sys/nfs41sys_openclose.c
- index 93c95b1..c863b6c 100644
- --- a/sys/nfs41sys_openclose.c
- +++ b/sys/nfs41sys_openclose.c
- @@ -574,11 +574,13 @@ NTSTATUS check_nfs41_create_args(
- goto out;
- }
- +#ifndef NFS41_STREAM_SUPPORT
- if (isStream(SrvOpen->pAlreadyPrefixedName)) {
- DbgP("nfs41_Create: Streams not supported (yet)\n");
- status = STATUS_NOT_SUPPORTED;
- goto out;
- }
- +#endif /* !NFS41_STREAM_SUPPORT */
- if (pVNetRootContext->read_only &&
- (params->DesiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
- diff --git a/tests/manual_testing.txt b/tests/manual_testing.txt
- index a520bfb..a04b49e 100644
- --- a/tests/manual_testing.txt
- +++ b/tests/manual_testing.txt
- @@ -1,5 +1,5 @@
- #
- -# ms-nfs41-client manual testing sequence, 2025-12-12
- +# ms-nfs41-client manual testing sequence, 2025-12-27
- #
- # Draft version, needs to be turned into automated tests
- # if possible
- @@ -218,6 +218,17 @@ $ rm -f d1 d2 d1.diff ; printf '1\n2\n' >d1 ; cp d1 d2 ; printf '3\n' >> d2 ; di
- The test should print "# test OK"
- +#
- +# Tests for Win32 streams
- +#
- +rm -Rf dir1 && mkdir dir1 && cmd /c 'echo dir_line1 >>dir1:dirstr1 & echo dir_line2 >>dir1:dirstr1 & cat <dir1:dirstr1'
- +rm -f file1 && touch file1 && cmd /c 'echo file_line1 >>file1:filestr1 & echo file_line1 >>file1:filestr1 & C:\cygwin64\bin\cat.exe <file1:filestr1'
- +rm -f file1 && touch file1 && cmd /c 'echo file_line1 >>file1:filestr1 & echo file_line1 >>file1:filestr1 & type file1:filestr1'
- +rm -f file1 && touch file1 && cmd /c 'echo file_line1 >>file1:filestr1 & echo file_line1 >>file1:filestr1 & type file1:filestr1:$DATA'
- +
- +# FIXME: tests for stream removal and enumeration
- +
- +
- #
- # Tests for Cycgwin/UWIN/SFU Nfs3Attr EA-based local uid/gid
- #
Win32 streams support
Posted by Anonymous on Sat 27th Dec 2025 18:55
raw | new post
view followups (newest first): Win32 streams support by Anonymous
Submit a correction or amendment below (click here to make a fresh posting)
After submitting an amendment, you'll be able to view the differences between the old and new posts easily.
nrubsig.kpaste.net RSS