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