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