s3: Plumb smb_filename through SMB_VFS_RENAME
[nivanova/samba-autobuild/.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, const char *path)
466 {
467         int ret;
468         bool is_stream;
469         char *base = NULL;
470         char *stream = NULL;
471         int dir_fd, saved_errno;
472
473         ret = onefs_is_stream(path, &base, &stream, &is_stream);
474         if (ret) {
475                 return ret;
476         }
477
478         if (!is_stream) {
479                 return SMB_VFS_NEXT_UNLINK(handle, path);
480         }
481
482         /* If it's the ::$DATA stream just unlink the base file name. */
483         if (!stream) {
484                 return SMB_VFS_NEXT_UNLINK(handle, base);
485         }
486
487         dir_fd = get_stream_dir_fd(handle->conn, base, NULL);
488         if (dir_fd < 0) {
489                 return -1;
490         }
491
492         ret = enc_unlinkat(dir_fd, stream, ENC_DEFAULT, 0);
493
494         saved_errno = errno;
495         close(dir_fd);
496         errno = saved_errno;
497         return ret;
498 }
499
500 int onefs_vtimes_streams(vfs_handle_struct *handle, const char *fname,
501                          int flags, struct timespec times[3])
502 {
503         int ret;
504         bool is_stream;
505         char *base;
506         char *stream;
507         int dirfd;
508         int saved_errno;
509
510         START_PROFILE(syscall_ntimes);
511
512         ret = onefs_is_stream(fname, &base, &stream, &is_stream);
513         if (ret)
514                 return ret;
515
516         if (!is_stream) {
517                 ret = vtimes(fname, times, flags);
518                 return ret;
519         }
520
521         dirfd = get_stream_dir_fd(handle->conn, base, NULL);
522         if (dirfd < -1) {
523                 return -1;
524         }
525
526         ret = enc_vtimesat(dirfd, stream, ENC_DEFAULT, times, flags);
527
528         END_PROFILE(syscall_ntimes);
529
530         saved_errno = errno;
531         close(dirfd);
532         errno = saved_errno;
533         return ret;
534 }
535
536 int onefs_chflags(vfs_handle_struct *handle, const char *path,
537                   unsigned int flags)
538 {
539         char *base = NULL;
540         char *stream = NULL;
541
542         if (!NT_STATUS_IS_OK(onefs_split_ntfs_stream_name(talloc_tos(), path,
543                                                           &base, &stream))) {
544                 DEBUG(10, ("onefs_split_ntfs_stream_name failed\n"));
545                 errno = ENOMEM;
546                 return -1;
547         }
548
549         /*
550          * Only set the attributes on the base file.  ifs_createfile handles
551          * file creation attribute semantics.
552          */
553         return SMB_VFS_NEXT_CHFLAGS(handle, base, flags);
554 }
555
556 /*
557  * Streaminfo enumeration functionality
558  */
559 struct streaminfo_state {
560         TALLOC_CTX *mem_ctx;
561         vfs_handle_struct *handle;
562         unsigned int num_streams;
563         struct stream_struct *streams;
564         NTSTATUS status;
565 };
566
567 static bool add_one_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
568                            struct stream_struct **streams,
569                            const char *name, SMB_OFF_T size,
570                            SMB_OFF_T alloc_size)
571 {
572         struct stream_struct *tmp;
573
574         tmp = TALLOC_REALLOC_ARRAY(mem_ctx, *streams, struct stream_struct,
575                                    (*num_streams)+1);
576         if (tmp == NULL) {
577                 return false;
578         }
579
580         tmp[*num_streams].name = talloc_asprintf(mem_ctx, ":%s:%s", name,
581                                                  "$DATA");
582         if (tmp[*num_streams].name == NULL) {
583                 return false;
584         }
585
586         tmp[*num_streams].size = size;
587         tmp[*num_streams].alloc_size = alloc_size;
588
589         *streams = tmp;
590         *num_streams += 1;
591         return true;
592 }
593
594 static NTSTATUS walk_onefs_streams(connection_struct *conn, files_struct *fsp,
595                                    const char *fname,
596                                    struct streaminfo_state *state,
597                                    SMB_STRUCT_STAT *base_sbuf)
598 {
599         NTSTATUS status = NT_STATUS_OK;
600         bool opened_base_fd = false;
601         int base_fd = -1;
602         int dir_fd = -1;
603         int stream_fd = -1;
604         int ret;
605         SMB_STRUCT_DIR *dirp = NULL;
606         SMB_STRUCT_DIRENT *dp = NULL;
607         files_struct fake_fs;
608         struct fd_handle fake_fh;
609         SMB_STRUCT_STAT stream_sbuf;
610
611         ZERO_STRUCT(fake_fh);
612         ZERO_STRUCT(fake_fs);
613
614         /* If the base file is already open, use its fd. */
615         if ((fsp != NULL) && (fsp->fh->fd != -1)) {
616                 base_fd = fsp->fh->fd;
617         } else {
618                 opened_base_fd = true;
619         }
620
621         dir_fd = get_stream_dir_fd(conn, fname, &base_fd);
622         if (dir_fd < 0) {
623                 return map_nt_error_from_unix(errno);
624         }
625
626         /* Open the ADS directory. */
627         if ((dirp = fdopendir(dir_fd)) == NULL) {
628                 DEBUG(0, ("Error on opendir %s. errno=%d (%s)\n",
629                           fname, errno, strerror(errno)));
630                 status = map_nt_error_from_unix(errno);
631                 goto out;
632         }
633
634         /* Initialize the dir state struct and add it to the list.
635          * This is a layer violation, and really should be handled by a
636          * VFS_FDOPENDIR() call which would properly setup the dir state.
637          * But since this is all within the onefs.so module, we cheat for
638          * now and call directly into the readdirplus code.
639          * NOTE: This state MUST be freed by a proper VFS_CLOSEDIR() call. */
640         ret = onefs_rdp_add_dir_state(conn, dirp);
641         if (ret) {
642                 DEBUG(0, ("Error adding dir_state to the list\n"));
643                 status = map_nt_error_from_unix(errno);
644                 goto out;
645         }
646
647         fake_fs.conn = conn;
648         fake_fs.fh = &fake_fh;
649         fake_fs.fsp_name = SMB_STRDUP(fname);
650
651         /* Iterate over the streams in the ADS directory. */
652         while ((dp = SMB_VFS_READDIR(conn, dirp, NULL)) != NULL) {
653                 /* Skip the "." and ".." entries */
654                 if ((strcmp(dp->d_name, ".") == 0) ||
655                     (strcmp(dp->d_name, "..") == 0))
656                         continue;
657
658                 /* Open actual stream */
659                 if ((stream_fd = onefs_sys_create_file(conn,
660                                                          base_fd,
661                                                          dp->d_name,
662                                                          0,
663                                                          0,
664                                                          0,
665                                                          0,
666                                                          0,
667                                                          0,
668                                                          INTERNAL_OPEN_ONLY,
669                                                          0,
670                                                          NULL,
671                                                          0,
672                                                          NULL)) == -1) {
673                         DEBUG(0, ("Error opening stream %s:%s. "
674                                   "errno=%d (%s)\n", fname, dp->d_name, errno,
675                                   strerror(errno)));
676                         continue;
677                 }
678
679                 /* Figure out the stat info. */
680                 fake_fh.fd = stream_fd;
681                 ret = SMB_VFS_FSTAT(&fake_fs, &stream_sbuf);
682                 close(stream_fd);
683
684                 if (ret) {
685                         DEBUG(0, ("Error fstating stream %s:%s. "
686                                   "errno=%d (%s)\n", fname, dp->d_name, errno,
687                                   strerror(errno)));
688                         continue;
689                 }
690
691                 merge_stat(&stream_sbuf, base_sbuf);
692
693                 if (!add_one_stream(state->mem_ctx,
694                                     &state->num_streams, &state->streams,
695                                     dp->d_name, stream_sbuf.st_ex_size,
696                                     SMB_VFS_GET_ALLOC_SIZE(conn, NULL,
697                                                            &stream_sbuf))) {
698                         state->status = NT_STATUS_NO_MEMORY;
699                         break;
700                 }
701         }
702
703 out:
704         /* Cleanup everything that was opened. */
705         if (dirp != NULL) {
706                 SMB_VFS_CLOSEDIR(conn, dirp);
707         }
708         if (dir_fd >= 0) {
709                 close(dir_fd);
710         }
711         if (opened_base_fd) {
712                 SMB_ASSERT(base_fd >= 0);
713                 close(base_fd);
714         }
715
716         SAFE_FREE(fake_fs.fsp_name);
717         return status;
718 }
719
720 NTSTATUS onefs_streaminfo(vfs_handle_struct *handle,
721                           struct files_struct *fsp,
722                           const char *fname,
723                           TALLOC_CTX *mem_ctx,
724                           unsigned int *num_streams,
725                           struct stream_struct **streams)
726 {
727         SMB_STRUCT_STAT sbuf;
728         int ret;
729         NTSTATUS status;
730         struct streaminfo_state state;
731
732         /* Get a valid stat. */
733         if ((fsp != NULL) && (fsp->fh->fd != -1)) {
734                 if (is_ntfs_stream_name(fsp->fsp_name)) {
735                         return NT_STATUS_INVALID_PARAMETER;
736                 }
737                 ret = SMB_VFS_FSTAT(fsp, &sbuf);
738         } else {
739                 struct smb_filename *smb_fname = NULL;
740
741                 if (is_ntfs_stream_name(fname)) {
742                         return NT_STATUS_INVALID_PARAMETER;
743                 }
744
745                 status = create_synthetic_smb_fname(talloc_tos(), fname, NULL,
746                                                     NULL, &smb_fname);
747                 if (!NT_STATUS_IS_OK(status)) {
748                         return status;
749                 }
750                 ret = SMB_VFS_STAT(handle->conn, smb_fname);
751
752                 sbuf = smb_fname->st;
753
754                 TALLOC_FREE(smb_fname);
755         }
756
757         if (ret == -1) {
758                 return map_nt_error_from_unix(errno);
759         }
760
761         state.streams = NULL;
762         state.num_streams = 0;
763
764         if (lp_parm_bool(SNUM(handle->conn), PARM_ONEFS_TYPE,
765                 PARM_IGNORE_STREAMS, PARM_IGNORE_STREAMS_DEFAULT)) {
766                 goto out;
767         }
768
769         /* Add the default stream. */
770         if (S_ISREG(sbuf.st_ex_mode)) {
771                 if (!add_one_stream(mem_ctx,
772                                     &state.num_streams, &state.streams,
773                                     "", sbuf.st_ex_size,
774                                     SMB_VFS_GET_ALLOC_SIZE(handle->conn, fsp,
775                                                            &sbuf))) {
776                         return NT_STATUS_NO_MEMORY;
777                 }
778         }
779
780         state.mem_ctx = mem_ctx;
781         state.handle = handle;
782         state.status = NT_STATUS_OK;
783
784         /* If there are more streams, add them too. */
785         if (sbuf.st_ex_flags & UF_HASADS) {
786
787                 status = walk_onefs_streams(handle->conn, fsp, fname,
788                     &state, &sbuf);
789
790                 if (!NT_STATUS_IS_OK(status)) {
791                         TALLOC_FREE(state.streams);
792                         return status;
793                 }
794
795                 if (!NT_STATUS_IS_OK(state.status)) {
796                         TALLOC_FREE(state.streams);
797                         return state.status;
798                 }
799         }
800  out:
801         *num_streams = state.num_streams;
802         *streams = state.streams;
803         return NT_STATUS_OK;
804 }