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