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