pastebin - collaborative debugging tool
nrubsig.kpaste.net RSS


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

  1. diff --git a/cygwin/devel/msnfs41client.bash b/cygwin/devel/msnfs41client.bash
  2. index c963e45..7152bfc 100755
  3. --- a/cygwin/devel/msnfs41client.bash
  4. +++ b/cygwin/devel/msnfs41client.bash
  5. @@ -690,7 +690,7 @@ function nfsclient_rundeamon
  6.                         "${nfsd_args[@]}"
  7.                 )
  8.                 "${nfsd_args[@]}"
  9. -       elif false ; then
  10. +       elif true ; then
  11.                 export _NT_ALT_SYMBOL_PATH="$(cygpath -w "$PWD");srv*https://msdl.microsoft.com/download/symbols"
  12.                 #
  13.                 # Useful cdb cmds:
  14. @@ -830,7 +830,7 @@ function nfsclient_system_rundeamon
  15.                 )
  16.  
  17.                 "${nfsd_args[@]}"
  18. -       elif false ; then
  19. +       elif true ; then
  20.                 export _NT_ALT_SYMBOL_PATH="$(cygpath -w "${sbinpath}");srv*https://msdl.microsoft.com/download/symbols"
  21.                 # - heap tests (eats lots of memory):
  22.                 # '-c' '!gflag +full;g' for heap tests
  23. diff --git a/daemon/fileinfoutil.c b/daemon/fileinfoutil.c
  24. index 0ed40c8..653f2bb 100644
  25. --- a/daemon/fileinfoutil.c
  26. +++ b/daemon/fileinfoutil.c
  27. @@ -59,7 +59,7 @@ ULONG nfs_file_info_to_attributes(
  28.          if (info->symlink_dir)
  29.              attrs |= FILE_ATTRIBUTE_DIRECTORY;
  30.      }
  31. -    else if (info->type == NF4REG) {
  32. +    else if (info->type == NF4REG || info->type == NF4NAMEDATTR) {
  33.          if (superblock->sparse_file_support) {
  34.              /* FIXME: What about pNFS ? */
  35.              attrs |= FILE_ATTRIBUTE_SPARSE_FILE;
  36. @@ -383,6 +383,7 @@ void nfs_to_stat_lx_info(
  37.      stat_lx_out->LxMode = 0UL;
  38.      switch(info->type) {
  39.          case NF4REG:
  40. +        case NF4NAMEDATTR:
  41.              stat_lx_out->LxMode |= LX_MODE_S_IFREG;
  42.              break;
  43.          case NF4DIR:
  44. diff --git a/daemon/getattr.c b/daemon/getattr.c
  45. index 4744285..a100470 100644
  46. --- a/daemon/getattr.c
  47. +++ b/daemon/getattr.c
  48. @@ -204,6 +204,9 @@ static int handle_getattr(void *daemon_context, nfs41_upcall *upcall)
  49.              &args->stat_lx_info);
  50.          break;
  51.  #endif /* NFS41_DRIVER_WSL_SUPPORT */
  52. +#ifdef NFS41_STREAM_SUPPORT
  53. +    case FileStreamInformation:
  54. +#endif /* NFS41_STREAM_SUPPORT */
  55.      default:
  56.          eprintf("handle_getattr(state->path.path='%s'): "
  57.              "unhandled file query class %d\n",
  58. diff --git a/daemon/lookup.c b/daemon/lookup.c
  59. index 3e9e591..48e4c1c 100644
  60. --- a/daemon/lookup.c
  61. +++ b/daemon/lookup.c
  62. @@ -502,6 +502,146 @@ int nfs41_lookup(
  63.  
  64.      DPRINTF(LULVL, ("--> nfs41_lookup('%s')\n", path.path));
  65.  
  66. +#ifdef NFS41_STREAM_SUPPORT
  67. +    /* Fast check for potential stream: Look for a colon */
  68. +    if (memchr(path_inout->path, ':', path_inout->len)) {
  69. +        char base_name[NFS41_MAX_PATH_LEN+1];
  70. +        char stream_name[NFS41_MAX_COMPONENT_LEN+1];
  71. +
  72. +        /* Parse the stream syntax */
  73. +        if (parse_win32stream_name(path.path, base_name, stream_name)) {
  74. +            if (stream_name[0] == '\0') {
  75. +                /* "foo:$DATA" case -> treat as "foo" */
  76. +                DPRINTF(0, ("nfs41_lookup: 'foo:$DATA' case -> treat as 'foo'\n"));
  77. +                size_t base_len = strlen(base_name);
  78. +
  79. +                (void)memcpy(path.path, base_name, base_len);
  80. +                path.path[base_len] = '\0';
  81. +                path.len = (unsigned short)base_len;
  82. +                path_pos = path.path;
  83. +                path_end = path.path + path.len;
  84. +
  85. +                /* Continue to normal lookup with stripped name */
  86. +            } else {
  87. +                /* "foo:bar" case */
  88. +
  89. +                /* 1. Recursively lookup the base file */
  90. +                nfs41_abs_path base_path = path;
  91. +                size_t base_len = strlen(base_name);
  92. +
  93. +                (void)memcpy(base_path.path, base_name, base_len);
  94. +                base_path.path[base_len] = '\0';
  95. +                base_path.len = (unsigned short)base_len;
  96. +
  97. +                nfs41_path_fh attr_target;
  98. +                if (target_out == NULL) {
  99. +                    target_out = &attr_target;
  100. +                }
  101. +
  102. +                status = nfs41_lookup(root, session, casesensitive, &base_path,
  103. +                    parent_out, target_out, info_out, session_out);
  104. +
  105. +                if (status == 0) {
  106. +                    /*
  107. +                     * Use the session returned by the base lookup if applicable
  108. +                     */
  109. +                    nfs41_session *lookup_session;
  110. +                    nfs41_compound compound;
  111. +                    nfs_argop4 argops[8];
  112. +                    nfs_resop4 resops[8];
  113. +                    nfs41_sequence_args sequence_args;
  114. +                    nfs41_sequence_res sequence_res;
  115. +                    nfs41_openattr_args openattr_args;
  116. +                    nfs41_openattr_res openattr_res;
  117. +                    nfs41_putfh_args putfh_args = { 0 };
  118. +                    nfs41_putfh_res putfh_res;
  119. +                    nfs41_lookup_args lookup_args = { 0 };
  120. +                    nfs41_lookup_res lookup_res;
  121. +                    nfs41_getfh_res getfh_res;
  122. +                    nfs41_getattr_args getattr_args = { 0 };
  123. +                    nfs41_getattr_res getattr_res = { 0 };
  124. +                    nfs41_component stream_comp = {
  125. +                        .name = stream_name,
  126. +                        .len = (unsigned short)strlen(stream_name)
  127. +                    };
  128. +                    /* Minimal attributes for stream */
  129. +                    bitmap4 attr_request = {
  130. +                        .count = 2,
  131. +                        .arr = {
  132. +                            [0] = FATTR4_WORD0_TYPE | FATTR4_WORD0_CHANGE |
  133. +                                FATTR4_WORD0_SIZE | FATTR4_WORD0_FSID |
  134. +                                FATTR4_WORD0_FILEID,
  135. +                            [1] = FATTR4_WORD1_MODE | FATTR4_WORD1_SPACE_USED |
  136. +                                FATTR4_WORD1_TIME_ACCESS |
  137. +                                FATTR4_WORD1_TIME_CREATE |
  138. +                                FATTR4_WORD1_TIME_MODIFY |
  139. +                                FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP,
  140. +                            [2] = 0
  141. +                        }
  142. +                    };
  143. +
  144. +                    DPRINTF(1,
  145. +                        ("nfs41_lookup: "
  146. +                        "Looking up base_name='%s'/stream_name='%s'\n",
  147. +                        base_name, stream_name));
  148. +
  149. +                    if ((session_out != NULL) && (*session_out != NULL))
  150. +                        lookup_session = *session_out;
  151. +                    else
  152. +                        lookup_session = session;
  153. +
  154. +                    compound_init(&compound,
  155. +                        lookup_session->client->root->nfsminorvers,
  156. +                        argops, resops, "lookup_win32stream");
  157. +
  158. +                    compound_add_op(&compound, OP_SEQUENCE, &sequence_args, &sequence_res);
  159. +                    nfs41_session_sequence(&sequence_args, lookup_session, 0);
  160. +
  161. +                    compound_add_op(&compound, OP_PUTFH, &putfh_args, &putfh_res);
  162. +                    putfh_args.file = target_out;
  163. +                    putfh_args.in_recovery = FALSE;
  164. +
  165. +                    compound_add_op(&compound, OP_OPENATTR, &openattr_args, &openattr_res);
  166. +                    openattr_args.createdir = FALSE;
  167. +
  168. +                    compound_add_op(&compound, OP_LOOKUP, &lookup_args, &lookup_res);
  169. +                    lookup_args.name = &stream_comp;
  170. +
  171. +                    compound_add_op(&compound, OP_GETFH, NULL, &getfh_res);
  172. +                    getfh_res.fh = &target_out->fh;
  173. +
  174. +                    compound_add_op(&compound, OP_GETATTR, &getattr_args, &getattr_res);
  175. +                    getattr_args.attr_request = &attr_request;
  176. +                    /* If caller wanted info, fill it; else dummy */
  177. +                    nfs41_file_info dummy_info;
  178. +                    if (info_out)
  179. +                        getattr_res.info = info_out;
  180. +                    else
  181. +                        getattr_res.info = &dummy_info;
  182. +                    getattr_res.obj_attributes.attr_vals_len = NFS4_OPAQUE_LIMIT_ATTR;
  183. +
  184. +                    status = compound_encode_send_decode(lookup_session, &compound, TRUE);
  185. +
  186. +                    if (status == 0)
  187. +                        status = compound.res.status;
  188. +
  189. +                    if (status == 0) {
  190. +                        /* We MUST return a |NF4NAMEDATTR|, and never a |NF4ATTRDIR| */
  191. +                        EASSERT(getattr_res.info->type != NF4ATTRDIR);
  192. +                        EASSERT(getattr_res.info->type == NF4NAMEDATTR);
  193. +                    }
  194. +                    else {
  195. +                        DPRINTF(1, ("nfs41_lookup: failed for attr, status=%d\n",
  196. +                            (int)status));
  197. +                    }
  198. +                }
  199. +
  200. +                goto out;
  201. +            }
  202. +        }
  203. +    }
  204. +#endif /* NFS41_STREAM_SUPPORT */
  205. +
  206.      if (parent_out == NULL) parent_out = &parent;
  207.      if (target_out == NULL) target_out = ⌖
  208.      parent_out->fh.len = target_out->fh.len = 0;
  209. diff --git a/daemon/nfs41_ops.c b/daemon/nfs41_ops.c
  210. index 9012db4..a0c1e6e 100644
  211. --- a/daemon/nfs41_ops.c
  212. +++ b/daemon/nfs41_ops.c
  213. @@ -453,8 +453,8 @@ int nfs41_open(
  214.  {
  215.      int status;
  216.      nfs41_compound compound;
  217. -    nfs_argop4 argops[8];
  218. -    nfs_resop4 resops[8];
  219. +    nfs_argop4 argops[12];
  220. +    nfs_resop4 resops[12];
  221.      nfs41_sequence_args sequence_args;
  222.      nfs41_sequence_res sequence_res;
  223.      nfs41_putfh_args putfh_args[2];
  224. @@ -471,10 +471,41 @@ int nfs41_open(
  225.      bool_t current_fh_is_dir;
  226.      bool_t already_delegated = delegation->type == OPEN_DELEGATE_READ
  227.          || delegation->type == OPEN_DELEGATE_WRITE;
  228. +#ifdef NFS41_STREAM_SUPPORT
  229. +    char base_buf[NFS41_MAX_COMPONENT_LEN];
  230. +    char stream_buf[NFS41_MAX_COMPONENT_LEN];
  231. +    bool is_stream = false;
  232. +    nfs41_component base_comp = {0};
  233. +    nfs41_component stream_comp = {0};
  234. +    nfs41_lookup_args lookup_args;
  235. +    nfs41_lookup_res lookup_res;
  236. +    nfs41_openattr_args openattr_args;
  237. +    nfs41_openattr_res openattr_res;
  238. +#endif /* NFS41_STREAM_SUPPORT */
  239.  
  240.      EASSERT_IS_VALID_NON_NULL_PTR(parent);
  241.      EASSERT_IS_VALID_NON_NULL_PTR(parent->fh.superblock);
  242.  
  243. +#ifdef NFS41_STREAM_SUPPORT
  244. +    if (file->name.name) {
  245. +        if ((memchr(file->name.name, ':', file->name.len) != NULL)) {
  246. +            if (parse_win32stream_name(file->name.name,
  247. +                base_buf, stream_buf)) {
  248. +                if (stream_buf[0] != '\0') {
  249. +                    is_stream = true;
  250. +                    base_comp.name = base_buf;
  251. +                    base_comp.len = (unsigned short)strlen(base_buf);
  252. +                    stream_comp.name = stream_buf;
  253. +                    stream_comp.len = (unsigned short)strlen(stream_buf);
  254. +                }
  255. +            } else {
  256. +                status = ERROR_INVALID_NAME;
  257. +                goto out;
  258. +            }
  259. +        }
  260. +    }
  261. +#endif /* NFS41_STREAM_SUPPORT */
  262. +
  263.      /* depending on the claim type, OPEN expects CURRENT_FH set
  264.       * to either the parent directory, or to the file itself */
  265.      switch (claim->claim) {
  266. @@ -524,6 +555,16 @@ int nfs41_open(
  267.          putfh_args[0].in_recovery = 0;
  268.      }
  269.  
  270. +#ifdef NFS41_STREAM_SUPPORT
  271. +    if (is_stream) {
  272. +        compound_add_op(&compound, OP_LOOKUP, &lookup_args, &lookup_res);
  273. +        lookup_args.name = &base_comp;
  274. +        compound_add_op(&compound, OP_OPENATTR, &openattr_args, &openattr_res);
  275. +        /* If we are creating the stream, we should allow creating the attr dir */
  276. +        openattr_args.createdir = (create == OPEN4_CREATE) ? TRUE : FALSE;
  277. +    }
  278. +#endif /* NFS41_STREAM_SUPPORT */
  279. +
  280.      compound_add_op(&compound, OP_OPEN, &open_args, &open_res);
  281.      open_args.seqid = 0;
  282.  #ifdef DISABLE_FILE_DELEGATIONS
  283. @@ -549,6 +590,18 @@ int nfs41_open(
  284.              parent->fh.superblock, &createattrs->attrmask);
  285.      }
  286.      open_args.claim = claim;
  287. +
  288. +#ifdef NFS41_STREAM_SUPPORT
  289. +    if (is_stream &&
  290. +        ((claim->claim == CLAIM_NULL) ||
  291. +            (claim->claim == CLAIM_DELEGATE_CUR))) {
  292. +        if (claim->claim == CLAIM_NULL)
  293. +            claim->u.null.filename = &stream_comp;
  294. +        else if (claim->claim == CLAIM_DELEGATE_CUR)
  295. +            claim->u.deleg_cur.name = &stream_comp;
  296. +    }
  297. +#endif /* NFS41_STREAM_SUPPORT */
  298. +
  299.      open_res.resok4.stateid = stateid;
  300.      open_res.resok4.delegation = delegation;
  301.  
  302. @@ -582,6 +635,7 @@ int nfs41_open(
  303.      if (compound_error(status = compound.res.status))
  304.          goto out;
  305.  
  306. +    /* This can happen if |nfs41_open()| is called by the EA code */
  307.      if (dir_info.type == NF4ATTRDIR) {
  308.          file->fh.superblock = parent->fh.superblock;
  309.          goto out;
  310. @@ -614,8 +668,8 @@ int nfs41_create(
  311.  {
  312.      int status;
  313.      nfs41_compound compound;
  314. -    nfs_argop4 argops[8];
  315. -    nfs_resop4 resops[8];
  316. +    nfs_argop4 argops[12];
  317. +    nfs_resop4 resops[12];
  318.      nfs41_sequence_args sequence_args;
  319.      nfs41_sequence_res sequence_res;
  320.      nfs41_putfh_args putfh_args;
  321. @@ -629,6 +683,37 @@ int nfs41_create(
  322.      nfs41_file_info dir_info;
  323.      nfs41_savefh_res savefh_res;
  324.      nfs41_restorefh_res restorefh_res;
  325. +#ifdef NFS41_STREAM_SUPPORT
  326. +    char base_buf[NFS41_MAX_COMPONENT_LEN];
  327. +    char stream_buf[NFS41_MAX_COMPONENT_LEN];
  328. +    bool is_stream = false;
  329. +    nfs41_component base_comp = {0};
  330. +    nfs41_component stream_comp = {0};
  331. +    nfs41_lookup_args lookup_args;
  332. +    nfs41_lookup_res lookup_res;
  333. +    nfs41_openattr_args openattr_args;
  334. +    nfs41_openattr_res openattr_res;
  335. +#endif /* NFS41_STREAM_SUPPORT */
  336. +
  337. +#ifdef NFS41_STREAM_SUPPORT
  338. +    if (file->name.name) {
  339. +        if ((memchr(file->name.name, ':', file->name.len) != NULL)) {
  340. +            if (parse_win32stream_name(file->name.name,
  341. +                base_buf, stream_buf)) {
  342. +                if (stream_buf[0] != '\0') {
  343. +                    is_stream = true;
  344. +                    base_comp.name = base_buf;
  345. +                    base_comp.len = (unsigned short)strlen(base_buf);
  346. +                    stream_comp.name = stream_buf;
  347. +                    stream_comp.len = (unsigned short)strlen(stream_buf);
  348. +                }
  349. +            } else {
  350. +                status = ERROR_INVALID_NAME;
  351. +                goto out;
  352. +            }
  353. +        }
  354. +    }
  355. +#endif /* NFS41_STREAM_SUPPORT */
  356.  
  357.      nfs41_superblock_getattr_mask(parent->fh.superblock, &attr_request);
  358.  
  359. @@ -644,13 +729,31 @@ int nfs41_create(
  360.  
  361.      compound_add_op(&compound, OP_SAVEFH, NULL, &savefh_res);
  362.  
  363. +#ifdef NFS41_STREAM_SUPPORT
  364. +    if (is_stream) {
  365. +        compound_add_op(&compound, OP_LOOKUP, &lookup_args, &lookup_res);
  366. +        lookup_args.name = &base_comp;
  367. +        compound_add_op(&compound, OP_OPENATTR, &openattr_args, &openattr_res);
  368. +        openattr_args.createdir = TRUE;
  369. +    }
  370. +#endif /* NFS41_STREAM_SUPPORT */
  371. +
  372.      compound_add_op(&compound, OP_CREATE, &create_args, &create_res);
  373.      create_args.objtype.type = type;
  374.      if (type == NF4LNK) {
  375.          create_args.objtype.u.lnk.linkdata = symlink;
  376.          create_args.objtype.u.lnk.linkdata_len = (uint32_t)strlen(symlink);
  377.      }
  378. -    create_args.name = &file->name;
  379. +
  380. +#ifdef NFS41_STREAM_SUPPORT
  381. +    if (is_stream) {
  382. +        create_args.name = &stream_comp;
  383. +    }
  384. +    else
  385. +#endif /* NFS41_STREAM_SUPPORT */
  386. +    {
  387. +        create_args.name = &file->name;
  388. +    }
  389.      create_args.createattrs = createattrs;
  390.      nfs41_superblock_supported_attrs(
  391.                  parent->fh.superblock, &createattrs->attrmask);
  392. @@ -1283,8 +1386,8 @@ int nfs41_remove(
  393.  {
  394.      int status;
  395.      nfs41_compound compound;
  396. -    nfs_argop4 argops[4];
  397. -    nfs_resop4 resops[4];
  398. +    nfs_argop4 argops[8];
  399. +    nfs_resop4 resops[8];
  400.      nfs41_sequence_args sequence_args;
  401.      nfs41_sequence_res sequence_res;
  402.      nfs41_putfh_args putfh_args;
  403. @@ -1295,6 +1398,35 @@ int nfs41_remove(
  404.      nfs41_getattr_res getattr_res NDSH(= { 0 });
  405.      bitmap4 attr_request;
  406.      nfs41_file_info info;
  407. +#ifdef NFS41_STREAM_SUPPORT
  408. +    char base_buf[NFS41_MAX_COMPONENT_LEN];
  409. +    char stream_buf[NFS41_MAX_COMPONENT_LEN];
  410. +    bool is_stream = false;
  411. +    nfs41_component base_comp = {0};
  412. +    nfs41_component stream_comp = {0};
  413. +    nfs41_lookup_args lookup_args;
  414. +    nfs41_lookup_res lookup_res;
  415. +    nfs41_openattr_args openattr_args;
  416. +    nfs41_openattr_res openattr_res;
  417. +
  418. +    if (target->name) {
  419. +        if ((memchr(target->name, ':', target->len) != NULL)) {
  420. +            if (parse_win32stream_name(target->name,
  421. +                base_buf, stream_buf)) {
  422. +                if (stream_buf[0] != '\0') {
  423. +                    is_stream = true;
  424. +                    base_comp.name = base_buf;
  425. +                    base_comp.len = (unsigned short)strlen(base_buf);
  426. +                    stream_comp.name = stream_buf;
  427. +                    stream_comp.len = (unsigned short)strlen(stream_buf);
  428. +                }
  429. +            } else {
  430. +                status = ERROR_INVALID_NAME;
  431. +                goto out;
  432. +            }
  433. +        }
  434. +    }
  435. +#endif /* NFS41_STREAM_SUPPORT */
  436.  
  437.      nfs41_superblock_getattr_mask(parent->fh.superblock, &attr_request);
  438.  
  439. @@ -1308,8 +1440,25 @@ int nfs41_remove(
  440.      putfh_args.file = parent;
  441.      putfh_args.in_recovery = 0;
  442.  
  443. +#ifdef NFS41_STREAM_SUPPORT
  444. +    if (is_stream) {
  445. +        compound_add_op(&compound, OP_LOOKUP, &lookup_args, &lookup_res);
  446. +        lookup_args.name = &base_comp;
  447. +        compound_add_op(&compound, OP_OPENATTR, &openattr_args, &openattr_res);
  448. +        openattr_args.createdir = FALSE;
  449. +    }
  450. +#endif /* NFS41_STREAM_SUPPORT */
  451. +
  452.      compound_add_op(&compound, OP_REMOVE, &remove_args, &remove_res);
  453. -    remove_args.target = target;
  454. +#ifdef NFS41_STREAM_SUPPORT
  455. +    if (is_stream) {
  456. +        remove_args.target = &stream_comp;
  457. +    }
  458. +    else
  459. +#endif /* NFS41_STREAM_SUPPORT */
  460. +    {
  461. +        remove_args.target = target;
  462. +    }
  463.  
  464.      compound_add_op(&compound, OP_GETATTR, &getattr_args, &getattr_res);
  465.      getattr_args.attr_request = &attr_request;
  466. diff --git a/daemon/nfs41_superblock.c b/daemon/nfs41_superblock.c
  467. index 5ffb171..75a54a6 100644
  468. --- a/daemon/nfs41_superblock.c
  469. +++ b/daemon/nfs41_superblock.c
  470. @@ -303,8 +303,12 @@ void nfs41_superblock_fs_attributes(
  471.          FsAttrs->FileSystemAttributes |= FILE_SUPPORTS_HARD_LINKS;
  472.      if (superblock->symlink_support)
  473.          FsAttrs->FileSystemAttributes |= FILE_SUPPORTS_REPARSE_POINTS;
  474. -    if (superblock->ea_support)
  475. +    if (superblock->ea_support) {
  476.          FsAttrs->FileSystemAttributes |= FILE_SUPPORTS_EXTENDED_ATTRIBUTES;
  477. +#ifdef NFS41_STREAM_SUPPORT
  478. +        FsAttrs->FileSystemAttributes |= FILE_NAMED_STREAMS;
  479. +#endif /* NFS41_STREAM_SUPPORT */
  480. +    }
  481.      if (superblock->case_preserving)
  482.          FsAttrs->FileSystemAttributes |= FILE_CASE_PRESERVED_NAMES;
  483.      if (!superblock->case_insensitive)
  484. diff --git a/daemon/open.c b/daemon/open.c
  485. index b5e72c7..4928bf7 100644
  486. --- a/daemon/open.c
  487. +++ b/daemon/open.c
  488. @@ -880,8 +880,12 @@ static int handle_open(void *daemon_context, nfs41_upcall *upcall)
  489.      // first check if windows told us it's a directory
  490.      if (args->create_opts & FILE_DIRECTORY_FILE)
  491.          state->type = NF4DIR;
  492. -    else
  493. -        state->type = NF4REG;
  494. +    else {
  495. +        if (strstr(args->path, ":"))
  496. +            state->type = NF4NAMEDATTR;
  497. +        else
  498. +            state->type = NF4REG;
  499. +    }
  500.  
  501.  #ifdef NFS41_DRIVER_ALLOW_CREATEFILE_ACLS
  502.      if (args->sec_desc_len) {
  503. @@ -1007,20 +1011,26 @@ static int handle_open(void *daemon_context, nfs41_upcall *upcall)
  504.                      goto out_free_state;
  505.                  }
  506.              }
  507. -        } else if (info.type == NF4REG) {
  508. +        } else if (info.type == NF4REG || info.type == NF4NAMEDATTR) {
  509.              DPRINTF(2, ("handle nfs41_open: FILE\n"));
  510.              if (args->create_opts & FILE_DIRECTORY_FILE) {
  511. -                DPRINTF(1, ("trying to open file '%s' as a directory\n",
  512. -                    state->path.path));
  513. +                DPRINTF(1,
  514. +                    ("trying to open file state->path.path='%s',"
  515. +                    "info.type=%d as a directory\n",
  516. +                    state->path.path, (int)info.type));
  517.                  /*
  518.                   * Notes:
  519.                   * - SMB returns |STATUS_OBJECT_TYPE_MISMATCH|
  520.                   * while NTFS returns |STATUS_NOT_A_DIRECTORY|
  521.                   * - See |map_open_errors()| for the mapping to
  522.                   * |STATUS_*|
  523. +                 * - We do not return |ERROR_DIRECTORY| when opening a Win32
  524. +                 * stream
  525.                   */
  526. -                status = ERROR_DIRECTORY;
  527. -                goto out_free_state;
  528. +                if (memchr(state->path.path, ':', state->path.len) == NULL) {
  529. +                    status = ERROR_DIRECTORY;
  530. +                    goto out_free_state;
  531. +                }
  532.              }
  533.          } else if (info.type == NF4LNK) {
  534.              DPRINTF(2, ("handle nfs41_open: SYMLINK\n"));
  535. @@ -1324,7 +1334,7 @@ supersede_retry:
  536.  
  537.  #ifdef DEBUG_OPEN_SPARSE_FILES
  538.      if ((status == 0) &&
  539. -        (info.type == NF4REG) &&
  540. +        (info.type == NF4REG | info.type == NF4NAMEDATTR) &&
  541.          (state->session->client->root->supports_nfs42_seek)) {
  542.          //debug_list_sparsefile_holes(state);
  543.          debug_list_sparsefile_datasections(state);
  544. @@ -1524,7 +1534,7 @@ static int handle_close(void *deamon_context, nfs41_upcall *upcall)
  545.      nfs41_open_state *state = upcall->state_ref;
  546.  
  547.      /* return associated file layouts if necessary */
  548. -    if (state->type == NF4REG)
  549. +    if (state->type == NF4REG || state->type == NF4NAMEDATTR)
  550.          pnfs_layout_state_close(state->session, state, args->remove);
  551.  
  552.      if (state->srv_open == args->srv_open)
  553. diff --git a/daemon/util.c b/daemon/util.c
  554. index 8d7f950..10bf3cc 100644
  555. --- a/daemon/util.c
  556. +++ b/daemon/util.c
  557. @@ -922,3 +922,85 @@ create_symlink_chgrp_out:
  558.      return chgrp_status;
  559.  }
  560.  #endif /* NFS41_DRIVER_SETGID_NEWGRP_SUPPORT */
  561. +
  562. +#ifdef NFS41_STREAM_SUPPORT
  563. +/*
  564. + * Helper to parse Win32 stream names.
  565. + * Returns |true| if a stream syntax is detected, |false| otherwise.
  566. + * On success:
  567. + * - |base_name|: set to the base file name
  568. + * - |stream_name|: set to the stream name, or |stream_name[0]=='\0'|
  569. + * if it was the default stream (:$DATA).
  570. + *
  571. + * - FIXME: We should implement support for stream types, e.g.
  572. + * "filename:streamname:type", e.g. "foo:bar:$DATA", "foo::$DATA"
  573. + * (for the default stream)
  574. + */
  575. +bool parse_win32stream_name(
  576. +    IN const char *restrict path,
  577. +    OUT char *restrict base_name,
  578. +    OUT char *restrict stream_name)
  579. +{
  580. +    const char *colon = strrchr(path, ':');
  581. +    const char *backslash = strrchr(path, '\\');
  582. +#if 1
  583. +    const char *data_suffix = ":$DATA";
  584. +    size_t path_len = strlen(path);
  585. +#endif
  586. +    size_t base_len;
  587. +
  588. +    /* Win32 streams are only allowed on the leaf component of a path */
  589. +    if (colon && backslash && (colon < backslash))
  590. +        return false;
  591. +
  592. +    if (!colon)
  593. +        return false;
  594. +
  595. +#if 1
  596. +    /* Handle "foo:$DATA" case --> maps to "foo" */
  597. +    if ((path_len >= 6) &&
  598. +        _stricmp(path + path_len - 6, data_suffix) == 0) {
  599. +        /* Check if there is another colon before this suffix */
  600. +        const char *prev_colon = NULL;
  601. +        const char *p = path;
  602. +        while (p < path + path_len - 6) {
  603. +            if (*p == ':')
  604. +                prev_colon = p;
  605. +            p++;
  606. +        }
  607. +
  608. +        if (prev_colon && ((backslash == NULL) || (prev_colon > backslash))) {
  609. +            /* "foo:bar:$DATA" --> maps to stream "bar" on "foo" */
  610. +            base_len = prev_colon - path;
  611. +
  612. +            (void)memcpy(base_name, path, base_len);
  613. +            base_name[base_len] = '\0';
  614. +
  615. +            size_t stream_len = (path + path_len - 6) - (prev_colon + 1);
  616. +            (void)memcpy(stream_name, prev_colon + 1, stream_len);
  617. +            stream_name[stream_len] = '\0';
  618. +
  619. +            DPRINTF(1,
  620. +                ("parse_win32stream_name: "
  621. +                "type case: base_name='%s'/stream_name='%s'\n",
  622. +                base_name, stream_name));
  623. +            return true;
  624. +        }
  625. +    }
  626. +#endif
  627. +
  628. +    /* Standard case: "foo:bar" */
  629. +    base_len = colon - path;
  630. +    (void)memcpy(base_name, path, base_len);
  631. +    base_name[base_len] = '\0';
  632. +
  633. +    (void)strcpy(stream_name, colon + 1);
  634. +
  635. +    DPRINTF(1,
  636. +        ("parse_win32stream_name: "
  637. +        "standard case: base_name='%s'/stream_name='%s'\n",
  638. +        base_name, stream_name));
  639. +
  640. +    return true;
  641. +}
  642. +#endif /* NFS41_STREAM_SUPPORT */
  643. diff --git a/daemon/util.h b/daemon/util.h
  644. index dc0ea07..a900a96 100644
  645. --- a/daemon/util.h
  646. +++ b/daemon/util.h
  647. @@ -458,4 +458,11 @@ int chgrp_to_primarygroup(
  648.      IN nfs41_open_state *state);
  649.  #endif /* NFS41_DRIVER_SETGID_NEWGRP_SUPPORT */
  650.  
  651. +#ifdef NFS41_STREAM_SUPPORT
  652. +bool parse_win32stream_name(
  653. +    IN const char *restrict path,
  654. +    OUT char *restrict base_name,
  655. +    OUT char *restrict stream_name);
  656. +#endif /* NFS41_STREAM_SUPPORT */
  657. +
  658.  #endif /* !__NFS41_DAEMON_UTIL_H__ */
  659. diff --git a/nfs41_build_features.h b/nfs41_build_features.h
  660. index fb744f5..4bcc154 100644
  661. --- a/nfs41_build_features.h
  662. +++ b/nfs41_build_features.h
  663. @@ -273,4 +273,10 @@
  664.   */
  665.  #define NFS41_DRIVER_ALLOW_CREATEFILE_ACLS 1
  666.  
  667. +/*
  668. + * |NFS41_STREAM_SUPPORT| - Enable Win32 named streams support using
  669. + * NFSv4.1 named attributes
  670. + */
  671. +#define NFS41_STREAM_SUPPORT 1
  672. +
  673.  #endif /* !_NFS41_DRIVER_BUILDFEATURES_ */
  674. diff --git a/sys/nfs41sys_fileinfo.c b/sys/nfs41sys_fileinfo.c
  675. index f871915..1647836 100644
  676. --- a/sys/nfs41sys_fileinfo.c
  677. +++ b/sys/nfs41sys_fileinfo.c
  678. @@ -398,6 +398,9 @@ NTSTATUS nfs41_QueryFileInformation(
  679.      case FileStatInformation:
  680.      case FileStatLxInformation:
  681.  #endif /* NFS41_DRIVER_WSL_SUPPORT */
  682. +#ifdef NFS41_STREAM_SUPPORT
  683. +    case FileStreamInformation:
  684. +#endif /* NFS41_STREAM_SUPPORT */
  685.          break;
  686.      default:
  687.          print_error("nfs41_QueryFileInformation: "
  688. @@ -500,6 +503,9 @@ NTSTATUS nfs41_QueryFileInformation(
  689.          case FileStatInformation:
  690.          case FileStatLxInformation:
  691.  #endif /* NFS41_DRIVER_WSL_SUPPORT */
  692. +#ifdef NFS41_STREAM_SUPPORT
  693. +        case FileStreamInformation:
  694. +#endif /* NFS41_STREAM_SUPPORT */
  695.              break;
  696.          default:
  697.              print_error("nfs41_QueryFileInformation: "
  698. diff --git a/sys/nfs41sys_openclose.c b/sys/nfs41sys_openclose.c
  699. index 93c95b1..c863b6c 100644
  700. --- a/sys/nfs41sys_openclose.c
  701. +++ b/sys/nfs41sys_openclose.c
  702. @@ -574,11 +574,13 @@ NTSTATUS check_nfs41_create_args(
  703.          goto out;
  704.      }
  705.  
  706. +#ifndef NFS41_STREAM_SUPPORT
  707.      if (isStream(SrvOpen->pAlreadyPrefixedName)) {
  708.          DbgP("nfs41_Create: Streams not supported (yet)\n");
  709.          status = STATUS_NOT_SUPPORTED;
  710.          goto out;
  711.      }
  712. +#endif /* !NFS41_STREAM_SUPPORT */
  713.  
  714.      if (pVNetRootContext->read_only &&
  715.              (params->DesiredAccess & (FILE_WRITE_DATA | FILE_APPEND_DATA))) {
  716. diff --git a/tests/manual_testing.txt b/tests/manual_testing.txt
  717. index a520bfb..a04b49e 100644
  718. --- a/tests/manual_testing.txt
  719. +++ b/tests/manual_testing.txt
  720. @@ -1,5 +1,5 @@
  721.  #
  722. -# ms-nfs41-client manual testing sequence, 2025-12-12
  723. +# ms-nfs41-client manual testing sequence, 2025-12-27
  724.  #
  725.  # Draft version, needs to be turned into automated tests
  726.  # if possible
  727. @@ -218,6 +218,17 @@ $ rm -f d1 d2 d1.diff ; printf '1\n2\n' >d1 ; cp d1 d2 ; printf '3\n' >> d2 ; di
  728.  The test should print "# test OK"
  729.  
  730.  
  731. +#
  732. +# Tests for Win32 streams
  733. +#
  734. +rm -Rf dir1 && mkdir dir1 && cmd /c 'echo dir_line1 >>dir1:dirstr1 & echo dir_line2 >>dir1:dirstr1 & cat <dir1:dirstr1'
  735. +rm -f file1 && touch file1 && cmd /c 'echo file_line1 >>file1:filestr1 & echo file_line1 >>file1:filestr1 & C:\cygwin64\bin\cat.exe <file1:filestr1'
  736. +rm -f file1 && touch file1 && cmd /c 'echo file_line1 >>file1:filestr1 & echo file_line1 >>file1:filestr1 & type file1:filestr1'
  737. +rm -f file1 && touch file1 && cmd /c 'echo file_line1 >>file1:filestr1 & echo file_line1 >>file1:filestr1 & type file1:filestr1:$DATA'
  738. +
  739. +# FIXME: tests for stream removal and enumeration
  740. +
  741. +
  742.  #
  743.  # Tests for Cycgwin/UWIN/SFU Nfs3Attr EA-based local uid/gid
  744.  #

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.

Syntax highlighting:

To highlight particular lines, prefix each line with {%HIGHLIGHT}




All content is user-submitted.
The administrators of this site (kpaste.net) are not responsible for their content.
Abuse reports should be emailed to us at