s3 onefs: Plumb smb_filename through onefs createfile path
[ira/wip.git] / source3 / modules / onefs_streams.c
1 /*
2  * Unix SMB/CIFS implementation.
3  *
4  * Support for OneFS Alternate Data Streams
5  *
6  * Copyright (C) Tim Prouty, 2008
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include "includes.h"
23 #include "onefs.h"
24 #include "onefs_config.h"
25
26 #include <sys/isi_enc.h>
27
28 /*
29  * OneFS stores streams without the explicit :$DATA at the end, so this strips
30  * it off.  All onefs_stream functions must call through this instead of
31  * split_ntfs_stream_name directly.
32  */
33 NTSTATUS onefs_split_ntfs_stream_name(TALLOC_CTX *mem_ctx, const char *fname,
34                                       char **pbase, char **pstream)
35 {
36         NTSTATUS status;
37         char *stream;
38
39         status = split_ntfs_stream_name(mem_ctx, fname, pbase, pstream);
40         if (!NT_STATUS_IS_OK(status)) {
41                 return status;
42         }
43
44         /* Default $DATA stream.  */
45         if (pstream == NULL || *pstream == NULL) {
46                 return NT_STATUS_OK;
47         }
48
49         /* Strip off the $DATA. */
50         stream = strrchr_m(*pstream, ':');
51         SMB_ASSERT(stream);
52         stream[0] = '\0';
53
54         return NT_STATUS_OK;
55 }
56
57 NTSTATUS onefs_stream_prep_smb_fname(TALLOC_CTX *ctx,
58                                      const struct smb_filename *smb_fname_in,
59                                      struct smb_filename **smb_fname_out)
60 {
61         char *stream_name = NULL;
62         NTSTATUS status;
63
64         /*
65          * Only attempt to strip off the trailing :$DATA if there is an actual
66          * stream there.  If it is the default stream, the smb_fname_out will
67          * just have a NULL stream so the base file is opened.
68          */
69         if (smb_fname_in->stream_name &&
70             !is_ntfs_default_stream_smb_fname(smb_fname_in)) {
71                 char *str_tmp = smb_fname_in->stream_name;
72
73                 /* First strip off the leading ':' */
74                 if (str_tmp[0] == ':') {
75                         str_tmp++;
76                 }
77
78                 /* Create a new copy of the stream_name. */
79                 stream_name = talloc_strdup(ctx, str_tmp);
80                 if (stream_name == NULL) {
81                         return NT_STATUS_NO_MEMORY;
82                 }
83
84                 /* Strip off the :$DATA if one exists. */
85                 str_tmp = strrchr_m(stream_name, ':');
86                 if (str_tmp) {
87                         str_tmp[0] = '\0';
88                 }
89         }
90
91         /*
92          * If there was a stream that wasn't the default stream the leading
93          * colon and trailing :$DATA has now been stripped off.  Create a new
94          * smb_filename to pass back.
95          */
96         status = create_synthetic_smb_fname(ctx, smb_fname_in->base_name,
97                                             stream_name, &smb_fname_in->st,
98                                             smb_fname_out);
99         TALLOC_FREE(stream_name);
100         return status;
101 }
102
103 int onefs_is_stream(const char *path, char **pbase, char **pstream,
104                     bool *is_stream)
105 {
106         (*is_stream) = is_ntfs_stream_name(path);
107
108         if (!(*is_stream)) {
109                 return 0;
110         }
111
112         if (!NT_STATUS_IS_OK(onefs_split_ntfs_stream_name(talloc_tos(), path,
113                                                           pbase, pstream))) {
114                 DEBUG(10, ("onefs_split_ntfs_stream_name failed\n"));
115                 errno = ENOMEM;
116                 return -1;
117         }
118
119         return 0;
120 }
121
122 int onefs_close(vfs_handle_struct *handle, struct files_struct *fsp)
123 {
124         int ret2, ret = 0;
125
126         if (fsp->base_fsp) {
127                 ret = SMB_VFS_NEXT_CLOSE(handle, fsp->base_fsp);
128         }
129         ret2 = SMB_VFS_NEXT_CLOSE(handle, fsp);
130
131         return ret ? ret : ret2;
132 }
133
134 /*
135  * Get the ADS directory fd for a file.
136  */
137 static int get_stream_dir_fd(connection_struct *conn, const char *base,
138                              int *base_fdp)
139 {
140         int base_fd;
141         int dir_fd;
142         int saved_errno;
143
144         /* If a valid base_fdp was given, use it. */
145         if (base_fdp && *base_fdp >= 0) {
146                 base_fd = *base_fdp;
147         } else {
148                 base_fd = onefs_sys_create_file(conn,
149                                                 -1,
150                                                 base,
151                                                 0,
152                                                 0,
153                                                 0,
154                                                 0,
155                                                 0,
156                                                 0,
157                                                 INTERNAL_OPEN_ONLY,
158                                                 0,
159                                                 NULL,
160                                                 0,
161                                                 NULL);
162                 if (base_fd < 0) {
163                         return -1;
164                 }
165         }
166
167         /* Open the ADS directory. */
168         dir_fd = onefs_sys_create_file(conn,
169                                         base_fd,
170                                         ".",
171                                         0,
172                                         FILE_READ_DATA,
173                                         0,
174                                         0,
175                                         0,
176                                         0,
177                                         INTERNAL_OPEN_ONLY,
178                                         0,
179                                         NULL,
180                                         0,
181                                         NULL);
182
183         /* Close base_fd if it's not need or on error. */
184         if (!base_fdp || dir_fd < 0) {
185                 saved_errno = errno;
186                 close(base_fd);
187                 errno = saved_errno;
188         }
189
190         /* Set the out base_fdp if successful and it was requested. */
191         if (base_fdp && dir_fd >= 0) {
192                 *base_fdp = base_fd;
193         }
194
195         return dir_fd;
196 }
197
198 int onefs_rename(vfs_handle_struct *handle, const char *oldname,
199                  const char *newname)
200 {
201         TALLOC_CTX *frame = NULL;
202         int ret = -1;
203         int dir_fd = -1;
204         int saved_errno;
205         bool old_is_stream;
206         bool new_is_stream;
207         char *obase = NULL;
208         char *osname = NULL;
209         char *nbase = NULL;
210         char *nsname = NULL;
211
212         START_PROFILE(syscall_rename_at);
213
214         frame = talloc_stackframe();
215
216         ret = onefs_is_stream(oldname, &obase, &osname, &old_is_stream);
217         if (ret) {
218                 END_PROFILE(syscall_rename_at);
219                 return ret;
220         }
221
222         ret = onefs_is_stream(newname, &nbase, &nsname, &new_is_stream);
223         if (ret) {
224                 END_PROFILE(syscall_rename_at);
225                 return ret;
226         }
227
228         if (!old_is_stream && !new_is_stream) {
229                 ret = SMB_VFS_NEXT_RENAME(handle, oldname, newname);
230                 END_PROFILE(syscall_rename_at);
231                 return ret;
232         }
233
234         dir_fd = get_stream_dir_fd(handle->conn, obase, NULL);
235         if (dir_fd < -1) {
236                 goto done;
237         }
238
239         DEBUG(8,("onefs_rename called for %s : %s  => %s : %s\n",
240                 obase, osname,  nbase, nsname));
241
242         /* Handle rename of stream to default stream specially. */
243         if (nsname == NULL) {
244                 ret = enc_renameat(dir_fd, osname, ENC_DEFAULT, AT_FDCWD,
245                                    nbase, ENC_DEFAULT);
246         } else {
247                 ret = enc_renameat(dir_fd, osname, ENC_DEFAULT, dir_fd, nsname,
248                                    ENC_DEFAULT);
249         }
250
251  done:
252         END_PROFILE(syscall_rename_at);
253
254         saved_errno = errno;
255         if (dir_fd >= 0) {
256                 close(dir_fd);
257         }
258         errno = saved_errno;
259         TALLOC_FREE(frame);
260         return ret;
261 }
262
263 /*
264  * Merge a base file's sbuf into the a streams's sbuf.
265  */
266 static void merge_stat(SMB_STRUCT_STAT *stream_sbuf,
267                        const SMB_STRUCT_STAT *base_sbuf)
268 {
269         int dos_flags = (UF_DOS_NOINDEX | UF_DOS_ARCHIVE |
270             UF_DOS_HIDDEN | UF_DOS_RO | UF_DOS_SYSTEM);
271         stream_sbuf->st_ex_mtime = base_sbuf->st_ex_mtime;
272         stream_sbuf->st_ex_ctime = base_sbuf->st_ex_ctime;
273         stream_sbuf->st_ex_atime = base_sbuf->st_ex_atime;
274         stream_sbuf->st_ex_flags &= ~dos_flags;
275         stream_sbuf->st_ex_flags |= base_sbuf->st_ex_flags & dos_flags;
276 }
277
278 /* fake timestamps */
279 static void onefs_adjust_stat_time(struct connection_struct *conn,
280                                    const char *fname, SMB_STRUCT_STAT *sbuf)
281 {
282         struct onefs_vfs_share_config cfg;
283         struct timeval tv_now = {0, 0};
284         bool static_mtime = False;
285         bool static_atime = False;
286
287         if (!onefs_get_config(SNUM(conn),
288                               ONEFS_VFS_CONFIG_FAKETIMESTAMPS, &cfg)) {
289                 return;
290         }
291
292         if (IS_MTIME_STATIC_PATH(conn, &cfg, fname)) {
293                 sbuf->st_ex_mtime = sbuf->st_ex_btime;
294                 static_mtime = True;
295         }
296         if (IS_ATIME_STATIC_PATH(conn, &cfg, fname)) {
297                 sbuf->st_ex_atime = sbuf->st_ex_btime;
298                 static_atime = True;
299         }
300
301         if (IS_CTIME_NOW_PATH(conn, &cfg, fname)) {
302                 if (cfg.ctime_slop < 0) {
303                         sbuf->st_ex_btime.tv_sec = INT_MAX - 1;
304                 } else {
305                         GetTimeOfDay(&tv_now);
306                         sbuf->st_ex_btime.tv_sec = tv_now.tv_sec +
307                             cfg.ctime_slop;
308                 }
309         }
310
311         if (!static_mtime && IS_MTIME_NOW_PATH(conn,&cfg,fname)) {
312                 if (cfg.mtime_slop < 0) {
313                         sbuf->st_ex_mtime.tv_sec = INT_MAX - 1;
314                 } else {
315                         if (tv_now.tv_sec == 0)
316                                 GetTimeOfDay(&tv_now);
317                         sbuf->st_ex_mtime.tv_sec = tv_now.tv_sec +
318                             cfg.mtime_slop;
319                 }
320         }
321         if (!static_atime && IS_ATIME_NOW_PATH(conn,&cfg,fname)) {
322                 if (cfg.atime_slop < 0) {
323                         sbuf->st_ex_atime.tv_sec = INT_MAX - 1;
324                 } else {
325                         if (tv_now.tv_sec == 0)
326                                 GetTimeOfDay(&tv_now);
327                         sbuf->st_ex_atime.tv_sec = tv_now.tv_sec +
328                             cfg.atime_slop;
329                 }
330         }
331 }
332
333 static int stat_stream(struct connection_struct *conn, const char *base,
334                        const char *stream, SMB_STRUCT_STAT *sbuf, int flags)
335 {
336         SMB_STRUCT_STAT base_sbuf;
337         int base_fd = -1, dir_fd, ret, saved_errno;
338
339         dir_fd = get_stream_dir_fd(conn, base, &base_fd);
340         if (dir_fd < 0) {
341                 return -1;
342         }
343
344         /* Stat the stream. */
345         ret = onefs_sys_fstat_at(dir_fd, stream, sbuf, flags);
346         if (ret != -1) {
347                 /* Now stat the base file and merge the results. */
348                 ret = onefs_sys_fstat(base_fd, &base_sbuf);
349                 if (ret != -1) {
350                         merge_stat(sbuf, &base_sbuf);
351                 }
352         }
353
354         saved_errno = errno;
355         close(dir_fd);
356         close(base_fd);
357         errno = saved_errno;
358         return ret;
359 }
360
361 int onefs_stat(vfs_handle_struct *handle, struct smb_filename *smb_fname)
362 {
363         struct smb_filename *smb_fname_onefs = NULL;
364         NTSTATUS status;
365         int ret;
366
367         status = onefs_stream_prep_smb_fname(talloc_tos(), smb_fname,
368                                              &smb_fname_onefs);
369         if (!NT_STATUS_IS_OK(status)) {
370                 errno = map_errno_from_nt_status(status);
371                 return -1;
372         }
373
374         /*
375          * If the smb_fname has no stream or is :$DATA, then just stat the
376          * base stream. Otherwise stat the stream.
377          */
378         if (!is_ntfs_stream_smb_fname(smb_fname_onefs)) {
379                 ret = onefs_sys_stat(smb_fname_onefs->base_name,
380                                      &smb_fname->st);
381         } else {
382                 ret = stat_stream(handle->conn, smb_fname_onefs->base_name,
383                                   smb_fname_onefs->stream_name, &smb_fname->st,
384                                   0);
385         }
386
387         onefs_adjust_stat_time(handle->conn, smb_fname->base_name,
388                                &smb_fname->st);
389
390         TALLOC_FREE(smb_fname_onefs);
391
392         return ret;
393 }
394
395 int onefs_fstat(vfs_handle_struct *handle, struct files_struct *fsp,
396                 SMB_STRUCT_STAT *sbuf)
397 {
398         SMB_STRUCT_STAT base_sbuf;
399         int ret;
400
401         /* Stat the stream, by calling next_fstat on the stream's fd. */
402         ret = onefs_sys_fstat(fsp->fh->fd, sbuf);
403         if (ret == -1) {
404                 return ret;
405         }
406
407         /* Stat the base file and merge the results. */
408         if (fsp != NULL && fsp->base_fsp != NULL) {
409                 ret = onefs_sys_fstat(fsp->base_fsp->fh->fd, &base_sbuf);
410                 if (ret != -1) {
411                         merge_stat(sbuf, &base_sbuf);
412                 }
413         }
414
415         onefs_adjust_stat_time(handle->conn, fsp->fsp_name, sbuf);
416         return ret;
417 }
418
419 int onefs_lstat(vfs_handle_struct *handle, struct smb_filename *smb_fname)
420 {
421         struct smb_filename *smb_fname_onefs = NULL;
422         NTSTATUS status;
423         int ret;
424
425         status = onefs_stream_prep_smb_fname(talloc_tos(), smb_fname,
426                                              &smb_fname_onefs);
427         if (!NT_STATUS_IS_OK(status)) {
428                 errno = map_errno_from_nt_status(status);
429                 return -1;
430         }
431
432         /*
433          * If the smb_fname has no stream or is :$DATA, then just stat the
434          * base stream. Otherwise stat the stream.
435          */
436         if (!is_ntfs_stream_smb_fname(smb_fname_onefs)) {
437                 ret = onefs_sys_lstat(smb_fname_onefs->base_name,
438                                       &smb_fname->st);
439         } else {
440                 ret = stat_stream(handle->conn, smb_fname_onefs->base_name,
441                                   smb_fname_onefs->stream_name, &smb_fname->st,
442                                   AT_SYMLINK_NOFOLLOW);
443         }
444
445         onefs_adjust_stat_time(handle->conn, smb_fname->base_name,
446                                &smb_fname->st);
447
448         TALLOC_FREE(smb_fname_onefs);
449
450         return ret;
451 }
452
453 int onefs_unlink(vfs_handle_struct *handle, const char *path)
454 {
455         int ret;
456         bool is_stream;
457         char *base = NULL;
458         char *stream = NULL;
459         int dir_fd, saved_errno;
460
461         ret = onefs_is_stream(path, &base, &stream, &is_stream);
462         if (ret) {
463                 return ret;
464         }
465
466         if (!is_stream) {
467                 return SMB_VFS_NEXT_UNLINK(handle, path);
468         }
469
470         /* If it's the ::$DATA stream just unlink the base file name. */
471         if (!stream) {
472                 return SMB_VFS_NEXT_UNLINK(handle, base);
473         }
474
475         dir_fd = get_stream_dir_fd(handle->conn, base, NULL);
476         if (dir_fd < 0) {
477                 return -1;
478         }
479
480         ret = enc_unlinkat(dir_fd, stream, ENC_DEFAULT, 0);
481
482         saved_errno = errno;
483         close(dir_fd);
484         errno = saved_errno;
485         return ret;
486 }
487
488 int onefs_vtimes_streams(vfs_handle_struct *handle, const char *fname,
489                          int flags, struct timespec times[3])
490 {
491         int ret;
492         bool is_stream;
493         char *base;
494         char *stream;
495         int dirfd;
496         int saved_errno;
497
498         START_PROFILE(syscall_ntimes);
499
500         ret = onefs_is_stream(fname, &base, &stream, &is_stream);
501         if (ret)
502                 return ret;
503
504         if (!is_stream) {
505                 ret = vtimes(fname, times, flags);
506                 return ret;
507         }
508
509         dirfd = get_stream_dir_fd(handle->conn, base, NULL);
510         if (dirfd < -1) {
511                 return -1;
512         }
513
514         ret = enc_vtimesat(dirfd, stream, ENC_DEFAULT, times, flags);
515
516         END_PROFILE(syscall_ntimes);
517
518         saved_errno = errno;
519         close(dirfd);
520         errno = saved_errno;
521         return ret;
522 }
523
524 int onefs_chflags(vfs_handle_struct *handle, const char *path,
525                   unsigned int flags)
526 {
527         char *base = NULL;
528         char *stream = NULL;
529
530         if (!NT_STATUS_IS_OK(onefs_split_ntfs_stream_name(talloc_tos(), path,
531                                                           &base, &stream))) {
532                 DEBUG(10, ("onefs_split_ntfs_stream_name failed\n"));
533                 errno = ENOMEM;
534                 return -1;
535         }
536
537         /*
538          * Only set the attributes on the base file.  ifs_createfile handles
539          * file creation attribute semantics.
540          */
541         return SMB_VFS_NEXT_CHFLAGS(handle, base, flags);
542 }
543
544 /*
545  * Streaminfo enumeration functionality
546  */
547 struct streaminfo_state {
548         TALLOC_CTX *mem_ctx;
549         vfs_handle_struct *handle;
550         unsigned int num_streams;
551         struct stream_struct *streams;
552         NTSTATUS status;
553 };
554
555 static bool add_one_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
556                            struct stream_struct **streams,
557                            const char *name, SMB_OFF_T size,
558                            SMB_OFF_T alloc_size)
559 {
560         struct stream_struct *tmp;
561
562         tmp = TALLOC_REALLOC_ARRAY(mem_ctx, *streams, struct stream_struct,
563                                    (*num_streams)+1);
564         if (tmp == NULL) {
565                 return false;
566         }
567
568         tmp[*num_streams].name = talloc_asprintf(mem_ctx, ":%s:%s", name,
569                                                  "$DATA");
570         if (tmp[*num_streams].name == NULL) {
571                 return false;
572         }
573
574         tmp[*num_streams].size = size;
575         tmp[*num_streams].alloc_size = alloc_size;
576
577         *streams = tmp;
578         *num_streams += 1;
579         return true;
580 }
581
582 static NTSTATUS walk_onefs_streams(connection_struct *conn, files_struct *fsp,
583                                    const char *fname,
584                                    struct streaminfo_state *state,
585                                    SMB_STRUCT_STAT *base_sbuf)
586 {
587         NTSTATUS status = NT_STATUS_OK;
588         bool opened_base_fd = false;
589         int base_fd = -1;
590         int dir_fd = -1;
591         int stream_fd = -1;
592         int ret;
593         SMB_STRUCT_DIR *dirp = NULL;
594         SMB_STRUCT_DIRENT *dp = NULL;
595         files_struct fake_fs;
596         struct fd_handle fake_fh;
597         SMB_STRUCT_STAT stream_sbuf;
598
599         ZERO_STRUCT(fake_fh);
600         ZERO_STRUCT(fake_fs);
601
602         /* If the base file is already open, use its fd. */
603         if ((fsp != NULL) && (fsp->fh->fd != -1)) {
604                 base_fd = fsp->fh->fd;
605         } else {
606                 opened_base_fd = true;
607         }
608
609         dir_fd = get_stream_dir_fd(conn, fname, &base_fd);
610         if (dir_fd < 0) {
611                 return map_nt_error_from_unix(errno);
612         }
613
614         /* Open the ADS directory. */
615         if ((dirp = fdopendir(dir_fd)) == NULL) {
616                 DEBUG(0, ("Error on opendir %s. errno=%d (%s)\n",
617                           fname, errno, strerror(errno)));
618                 status = map_nt_error_from_unix(errno);
619                 goto out;
620         }
621
622         /* Initialize the dir state struct and add it to the list.
623          * This is a layer violation, and really should be handled by a
624          * VFS_FDOPENDIR() call which would properly setup the dir state.
625          * But since this is all within the onefs.so module, we cheat for
626          * now and call directly into the readdirplus code.
627          * NOTE: This state MUST be freed by a proper VFS_CLOSEDIR() call. */
628         ret = onefs_rdp_add_dir_state(conn, dirp);
629         if (ret) {
630                 DEBUG(0, ("Error adding dir_state to the list\n"));
631                 status = map_nt_error_from_unix(errno);
632                 goto out;
633         }
634
635         fake_fs.conn = conn;
636         fake_fs.fh = &fake_fh;
637         fake_fs.fsp_name = SMB_STRDUP(fname);
638
639         /* Iterate over the streams in the ADS directory. */
640         while ((dp = SMB_VFS_READDIR(conn, dirp, NULL)) != NULL) {
641                 /* Skip the "." and ".." entries */
642                 if ((strcmp(dp->d_name, ".") == 0) ||
643                     (strcmp(dp->d_name, "..") == 0))
644                         continue;
645
646                 /* Open actual stream */
647                 if ((stream_fd = onefs_sys_create_file(conn,
648                                                          base_fd,
649                                                          dp->d_name,
650                                                          0,
651                                                          0,
652                                                          0,
653                                                          0,
654                                                          0,
655                                                          0,
656                                                          INTERNAL_OPEN_ONLY,
657                                                          0,
658                                                          NULL,
659                                                          0,
660                                                          NULL)) == -1) {
661                         DEBUG(0, ("Error opening stream %s:%s. "
662                                   "errno=%d (%s)\n", fname, dp->d_name, errno,
663                                   strerror(errno)));
664                         continue;
665                 }
666
667                 /* Figure out the stat info. */
668                 fake_fh.fd = stream_fd;
669                 ret = SMB_VFS_FSTAT(&fake_fs, &stream_sbuf);
670                 close(stream_fd);
671
672                 if (ret) {
673                         DEBUG(0, ("Error fstating stream %s:%s. "
674                                   "errno=%d (%s)\n", fname, dp->d_name, errno,
675                                   strerror(errno)));
676                         continue;
677                 }
678
679                 merge_stat(&stream_sbuf, base_sbuf);
680
681                 if (!add_one_stream(state->mem_ctx,
682                                     &state->num_streams, &state->streams,
683                                     dp->d_name, stream_sbuf.st_ex_size,
684                                     SMB_VFS_GET_ALLOC_SIZE(conn, NULL,
685                                                            &stream_sbuf))) {
686                         state->status = NT_STATUS_NO_MEMORY;
687                         break;
688                 }
689         }
690
691 out:
692         /* Cleanup everything that was opened. */
693         if (dirp != NULL) {
694                 SMB_VFS_CLOSEDIR(conn, dirp);
695         }
696         if (dir_fd >= 0) {
697                 close(dir_fd);
698         }
699         if (opened_base_fd) {
700                 SMB_ASSERT(base_fd >= 0);
701                 close(base_fd);
702         }
703
704         SAFE_FREE(fake_fs.fsp_name);
705         return status;
706 }
707
708 NTSTATUS onefs_streaminfo(vfs_handle_struct *handle,
709                           struct files_struct *fsp,
710                           const char *fname,
711                           TALLOC_CTX *mem_ctx,
712                           unsigned int *num_streams,
713                           struct stream_struct **streams)
714 {
715         SMB_STRUCT_STAT sbuf;
716         int ret;
717         NTSTATUS status;
718         struct streaminfo_state state;
719
720         /* Get a valid stat. */
721         if ((fsp != NULL) && (fsp->fh->fd != -1)) {
722                 if (is_ntfs_stream_name(fsp->fsp_name)) {
723                         return NT_STATUS_INVALID_PARAMETER;
724                 }
725                 ret = SMB_VFS_FSTAT(fsp, &sbuf);
726         } else {
727                 struct smb_filename *smb_fname = NULL;
728
729                 if (is_ntfs_stream_name(fname)) {
730                         return NT_STATUS_INVALID_PARAMETER;
731                 }
732
733                 status = create_synthetic_smb_fname(talloc_tos(), fname, NULL,
734                                                     NULL, &smb_fname);
735                 if (!NT_STATUS_IS_OK(status)) {
736                         return status;
737                 }
738                 ret = SMB_VFS_STAT(handle->conn, smb_fname);
739
740                 sbuf = smb_fname->st;
741
742                 TALLOC_FREE(smb_fname);
743         }
744
745         if (ret == -1) {
746                 return map_nt_error_from_unix(errno);
747         }
748
749         state.streams = NULL;
750         state.num_streams = 0;
751
752         if (lp_parm_bool(SNUM(handle->conn), PARM_ONEFS_TYPE,
753                 PARM_IGNORE_STREAMS, PARM_IGNORE_STREAMS_DEFAULT)) {
754                 goto out;
755         }
756
757         /* Add the default stream. */
758         if (S_ISREG(sbuf.st_ex_mode)) {
759                 if (!add_one_stream(mem_ctx,
760                                     &state.num_streams, &state.streams,
761                                     "", sbuf.st_ex_size,
762                                     SMB_VFS_GET_ALLOC_SIZE(handle->conn, fsp,
763                                                            &sbuf))) {
764                         return NT_STATUS_NO_MEMORY;
765                 }
766         }
767
768         state.mem_ctx = mem_ctx;
769         state.handle = handle;
770         state.status = NT_STATUS_OK;
771
772         /* If there are more streams, add them too. */
773         if (sbuf.st_ex_flags & UF_HASADS) {
774
775                 status = walk_onefs_streams(handle->conn, fsp, fname,
776                     &state, &sbuf);
777
778                 if (!NT_STATUS_IS_OK(status)) {
779                         TALLOC_FREE(state.streams);
780                         return status;
781                 }
782
783                 if (!NT_STATUS_IS_OK(state.status)) {
784                         TALLOC_FREE(state.streams);
785                         return state.status;
786                 }
787         }
788  out:
789         *num_streams = state.num_streams;
790         *streams = state.streams;
791         return NT_STATUS_OK;
792 }