Fix a bunch of compiler warnings about wrong format types.
[ira/wip.git] / source3 / modules / vfs_streams_xattr.c
1 /*
2  * Store streams in xattrs
3  *
4  * Copyright (C) Volker Lendecke, 2008
5  *
6  * Partly based on James Peach's Darwin module, which is
7  *
8  * Copyright (C) James Peach 2006-2007
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, see <http://www.gnu.org/licenses/>.
22  */
23
24 #include "includes.h"
25
26 #undef DBGC_CLASS
27 #define DBGC_CLASS DBGC_VFS
28
29 struct stream_io {
30         char *base;
31         char *xattr_name;
32         void *fsp_name_ptr;
33         files_struct *fsp;
34         vfs_handle_struct *handle;
35 };
36
37 static SMB_INO_T stream_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
38 {
39         struct MD5Context ctx;
40         unsigned char hash[16];
41         SMB_INO_T result;
42         char *upper_sname;
43
44         DEBUG(10, ("stream_inode called for %lu/%lu [%s]\n",
45                    (unsigned long)sbuf->st_dev,
46                    (unsigned long)sbuf->st_ino, sname));
47
48         upper_sname = talloc_strdup_upper(talloc_tos(), sname);
49         SMB_ASSERT(upper_sname != NULL);
50
51         MD5Init(&ctx);
52         MD5Update(&ctx, (unsigned char *)&(sbuf->st_dev),
53                   sizeof(sbuf->st_dev));
54         MD5Update(&ctx, (unsigned char *)&(sbuf->st_ino),
55                   sizeof(sbuf->st_ino));
56         MD5Update(&ctx, (unsigned char *)upper_sname,
57                   talloc_get_size(upper_sname)-1);
58         MD5Final(hash, &ctx);
59
60         TALLOC_FREE(upper_sname);
61
62         /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
63         memcpy(&result, hash, sizeof(result));
64
65         DEBUG(10, ("stream_inode returns %lu\n", (unsigned long)result));
66
67         return result;
68 }
69
70 static ssize_t get_xattr_size(connection_struct *conn,
71                                 files_struct *fsp,
72                                 const char *fname,
73                                 const char *xattr_name)
74 {
75         NTSTATUS status;
76         struct ea_struct ea;
77         ssize_t result;
78
79         status = get_ea_value(talloc_tos(), conn, fsp, fname,
80                               xattr_name, &ea);
81
82         if (!NT_STATUS_IS_OK(status)) {
83                 return -1;
84         }
85
86         result = ea.value.length-1;
87         TALLOC_FREE(ea.value.data);
88         return result;
89 }
90
91 static bool streams_xattr_recheck(struct stream_io *sio)
92 {
93         NTSTATUS status;
94         char *base = NULL;
95         char *sname = NULL;
96         char *xattr_name = NULL;
97
98         if (sio->fsp->fsp_name == sio->fsp_name_ptr) {
99                 return true;
100         }
101
102         status = split_ntfs_stream_name(talloc_tos(), sio->fsp->fsp_name,
103                                         &base, &sname);
104         if (!NT_STATUS_IS_OK(status)) {
105                 return false;
106         }
107
108         if (sname == NULL) {
109                 /* how can this happen */
110                 errno = EINVAL;
111                 return false;
112         }
113
114         xattr_name = talloc_asprintf(talloc_tos(), "%s%s",
115                                      SAMBA_XATTR_DOSSTREAM_PREFIX, sname);
116         if (xattr_name == NULL) {
117                 return false;
118         }
119
120         TALLOC_FREE(sio->xattr_name);
121         TALLOC_FREE(sio->base);
122         sio->xattr_name = talloc_strdup(VFS_MEMCTX_FSP_EXTENSION(sio->handle, sio->fsp),
123                                         xattr_name);
124         sio->base = talloc_strdup(VFS_MEMCTX_FSP_EXTENSION(sio->handle, sio->fsp),
125                                   base);
126         sio->fsp_name_ptr = sio->fsp->fsp_name;
127
128         if ((sio->xattr_name == NULL) || (sio->base == NULL)) {
129                 return false;
130         }
131
132         return true;
133 }
134
135 static int streams_xattr_fstat(vfs_handle_struct *handle, files_struct *fsp,
136                                SMB_STRUCT_STAT *sbuf)
137 {
138         int ret = -1;
139         struct stream_io *io = (struct stream_io *)
140                 VFS_FETCH_FSP_EXTENSION(handle, fsp);
141
142         DEBUG(10, ("streams_xattr_fstat called for %d\n", fsp->fh->fd));
143
144         if (io == NULL || fsp->base_fsp == NULL) {
145                 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
146         }
147
148         if (!streams_xattr_recheck(io)) {
149                 return -1;
150         }
151
152         if (lp_posix_pathnames()) {
153                 ret = SMB_VFS_LSTAT(handle->conn, io->base, sbuf);
154         } else {
155                 ret = SMB_VFS_STAT(handle->conn, io->base, sbuf);
156         }
157
158         if (ret == -1) {
159                 return -1;
160         }
161
162         sbuf->st_size = get_xattr_size(handle->conn, fsp->base_fsp,
163                                         io->base, io->xattr_name);
164         if (sbuf->st_size == -1) {
165                 return -1;
166         }
167
168         DEBUG(10, ("sbuf->st_size = %d\n", (int)sbuf->st_size));
169
170         sbuf->st_ino = stream_inode(sbuf, io->xattr_name);
171         sbuf->st_mode &= ~S_IFMT;
172         sbuf->st_mode |= S_IFREG;
173         sbuf->st_blocks = sbuf->st_size % STAT_ST_BLOCKSIZE + 1;
174
175         return 0;
176 }
177
178 static int streams_xattr_stat(vfs_handle_struct *handle, const char *fname,
179                               SMB_STRUCT_STAT *sbuf)
180 {
181         NTSTATUS status;
182         char *base = NULL, *sname = NULL;
183         int result = -1;
184         char *xattr_name;
185
186         if (!is_ntfs_stream_name(fname)) {
187                 return SMB_VFS_NEXT_STAT(handle, fname, sbuf);
188         }
189
190         status = split_ntfs_stream_name(talloc_tos(), fname, &base, &sname);
191         if (!NT_STATUS_IS_OK(status)) {
192                 errno = EINVAL;
193                 return -1;
194         }
195
196         if (sname == NULL){
197                 return SMB_VFS_NEXT_STAT(handle, base, sbuf);
198         }
199
200         if (SMB_VFS_STAT(handle->conn, base, sbuf) == -1) {
201                 goto fail;
202         }
203
204         xattr_name = talloc_asprintf(talloc_tos(), "%s%s",
205                                      SAMBA_XATTR_DOSSTREAM_PREFIX, sname);
206         if (xattr_name == NULL) {
207                 errno = ENOMEM;
208                 goto fail;
209         }
210
211         sbuf->st_size = get_xattr_size(handle->conn, NULL, base, xattr_name);
212         if (sbuf->st_size == -1) {
213                 errno = ENOENT;
214                 goto fail;
215         }
216
217         sbuf->st_ino = stream_inode(sbuf, xattr_name);
218         sbuf->st_mode &= ~S_IFMT;
219         sbuf->st_mode |= S_IFREG;
220         sbuf->st_blocks = sbuf->st_size % STAT_ST_BLOCKSIZE + 1;
221
222         result = 0;
223  fail:
224         TALLOC_FREE(base);
225         TALLOC_FREE(sname);
226         return result;
227 }
228
229 static int streams_xattr_lstat(vfs_handle_struct *handle, const char *fname,
230                                SMB_STRUCT_STAT *sbuf)
231 {
232         NTSTATUS status;
233         char *base, *sname;
234         int result = -1;
235         char *xattr_name;
236
237         if (!is_ntfs_stream_name(fname)) {
238                 return SMB_VFS_NEXT_LSTAT(handle, fname, sbuf);
239         }
240
241         status = split_ntfs_stream_name(talloc_tos(), fname, &base, &sname);
242         if (!NT_STATUS_IS_OK(status)) {
243                 errno = EINVAL;
244                 goto fail;
245         }
246
247         if (sname == NULL){
248                 return SMB_VFS_NEXT_LSTAT(handle, base, sbuf);
249         }
250
251         if (SMB_VFS_LSTAT(handle->conn, base, sbuf) == -1) {
252                 goto fail;
253         }
254
255         xattr_name = talloc_asprintf(talloc_tos(), "%s%s",
256                                      SAMBA_XATTR_DOSSTREAM_PREFIX, sname);
257         if (xattr_name == NULL) {
258                 errno = ENOMEM;
259                 goto fail;
260         }
261
262         sbuf->st_size = get_xattr_size(handle->conn, NULL, base, xattr_name);
263         if (sbuf->st_size == -1) {
264                 errno = ENOENT;
265                 goto fail;
266         }
267
268         sbuf->st_ino = stream_inode(sbuf, xattr_name);
269         sbuf->st_mode &= ~S_IFMT;
270         sbuf->st_mode |= S_IFREG;
271         sbuf->st_blocks = sbuf->st_size % STAT_ST_BLOCKSIZE + 1;
272
273         result = 0;
274  fail:
275         TALLOC_FREE(base);
276         TALLOC_FREE(sname);
277         return result;
278 }
279
280 static int streams_xattr_open(vfs_handle_struct *handle,  const char *fname,
281                               files_struct *fsp, int flags, mode_t mode)
282 {
283         TALLOC_CTX *frame;
284         NTSTATUS status;
285         struct stream_io *sio;
286         char *base, *sname;
287         struct ea_struct ea;
288         char *xattr_name;
289         int baseflags;
290         int hostfd = -1;
291
292         DEBUG(10, ("streams_xattr_open called for %s\n", fname));
293
294         if (!is_ntfs_stream_name(fname)) {
295                 return SMB_VFS_NEXT_OPEN(handle, fname, fsp, flags, mode);
296         }
297
298         frame = talloc_stackframe();
299
300         status = split_ntfs_stream_name(talloc_tos(), fname,
301                                         &base, &sname);
302         if (!NT_STATUS_IS_OK(status)) {
303                 errno = EINVAL;
304                 goto fail;
305         }
306
307         if (sname == NULL) {
308                 hostfd = SMB_VFS_NEXT_OPEN(handle, base, fsp, flags, mode);
309                 talloc_free(frame);
310                 return hostfd;
311         }
312
313         xattr_name = talloc_asprintf(talloc_tos(), "%s%s",
314                                      SAMBA_XATTR_DOSSTREAM_PREFIX, sname);
315         if (xattr_name == NULL) {
316                 errno = ENOMEM;
317                 goto fail;
318         }
319
320         /*
321          * We use baseflags to turn off nasty side-effects when opening the
322          * underlying file.
323          */
324         baseflags = flags;
325         baseflags &= ~O_TRUNC;
326         baseflags &= ~O_EXCL;
327         baseflags &= ~O_CREAT;
328
329         hostfd = SMB_VFS_OPEN(handle->conn, base, fsp, baseflags, mode);
330
331         /* It is legit to open a stream on a directory, but the base
332          * fd has to be read-only.
333          */
334         if ((hostfd == -1) && (errno == EISDIR)) {
335                 baseflags &= ~O_ACCMODE;
336                 baseflags |= O_RDONLY;
337                 hostfd = SMB_VFS_OPEN(handle->conn, fname, fsp, baseflags,
338                                       mode);
339         }
340
341         if (hostfd == -1) {
342                 goto fail;
343         }
344
345         status = get_ea_value(talloc_tos(), handle->conn, NULL, base,
346                               xattr_name, &ea);
347
348         DEBUG(10, ("get_ea_value returned %s\n", nt_errstr(status)));
349
350         if (!NT_STATUS_IS_OK(status)
351             && !NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
352                 /*
353                  * The base file is not there. This is an error even if we got
354                  * O_CREAT, the higher levels should have created the base
355                  * file for us.
356                  */
357                 DEBUG(10, ("streams_xattr_open: base file %s not around, "
358                            "returning ENOENT\n", base));
359                 errno = ENOENT;
360                 goto fail;
361         }
362
363         if (!NT_STATUS_IS_OK(status)) {
364                 /*
365                  * The attribute does not exist
366                  */
367
368                 if (flags & O_CREAT) {
369                         /*
370                          * Darn, xattrs need at least 1 byte
371                          */
372                         char null = '\0';
373
374                         DEBUG(10, ("creating attribute %s on file %s\n",
375                                    xattr_name, base));
376
377                         if (fsp->base_fsp->fh->fd != -1) {
378                                 if (SMB_VFS_FSETXATTR(
379                                         fsp->base_fsp, xattr_name,
380                                         &null, sizeof(null),
381                                         flags & O_EXCL ? XATTR_CREATE : 0) == -1) {
382                                         goto fail;
383                                 }
384                         } else {
385                                 if (SMB_VFS_SETXATTR(
386                                         handle->conn, base, xattr_name,
387                                         &null, sizeof(null),
388                                         flags & O_EXCL ? XATTR_CREATE : 0) == -1) {
389                                         goto fail;
390                                 }
391                         }
392                 }
393         }
394
395         if (flags & O_TRUNC) {
396                 char null = '\0';
397                 if (fsp->base_fsp->fh->fd != -1) {
398                         if (SMB_VFS_FSETXATTR(
399                                         fsp->base_fsp, xattr_name,
400                                         &null, sizeof(null),
401                                         flags & O_EXCL ? XATTR_CREATE : 0) == -1) {
402                                 goto fail;
403                         }
404                 } else {
405                         if (SMB_VFS_SETXATTR(
406                                         handle->conn, base, xattr_name,
407                                         &null, sizeof(null),
408                                         flags & O_EXCL ? XATTR_CREATE : 0) == -1) {
409                                 goto fail;
410                         }
411                 }
412         }
413
414         sio = (struct stream_io *)VFS_ADD_FSP_EXTENSION(handle, fsp,
415                                                         struct stream_io,
416                                                         NULL);
417         if (sio == NULL) {
418                 errno = ENOMEM;
419                 goto fail;
420         }
421
422         sio->xattr_name = talloc_strdup(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
423                                         xattr_name);
424         sio->base = talloc_strdup(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
425                                   base);
426         sio->fsp_name_ptr = fsp->fsp_name;
427         sio->handle = handle;
428         sio->fsp = fsp;
429
430         if ((sio->xattr_name == NULL) || (sio->base == NULL)) {
431                 errno = ENOMEM;
432                 goto fail;
433         }
434
435         TALLOC_FREE(frame);
436         return hostfd;
437
438  fail:
439         if (hostfd >= 0) {
440                 /*
441                  * BUGBUGBUG -- we would need to call fd_close_posix here, but
442                  * we don't have a full fsp yet
443                  */
444                 SMB_VFS_CLOSE(fsp);
445         }
446
447         TALLOC_FREE(frame);
448         return -1;
449 }
450
451 static int streams_xattr_unlink(vfs_handle_struct *handle,  const char *fname)
452 {
453         NTSTATUS status;
454         char *base = NULL;
455         char *sname = NULL;
456         int ret = -1;
457         char *xattr_name;
458
459         if (!is_ntfs_stream_name(fname)) {
460                 return SMB_VFS_NEXT_UNLINK(handle, fname);
461         }
462
463         status = split_ntfs_stream_name(talloc_tos(), fname, &base, &sname);
464         if (!NT_STATUS_IS_OK(status)) {
465                 errno = EINVAL;
466                 goto fail;
467         }
468
469         if (sname == NULL){
470                 return SMB_VFS_NEXT_UNLINK(handle, base);
471         }
472
473         xattr_name = talloc_asprintf(talloc_tos(), "%s%s",
474                                      SAMBA_XATTR_DOSSTREAM_PREFIX, sname);
475         if (xattr_name == NULL) {
476                 errno = ENOMEM;
477                 goto fail;
478         }
479
480         ret = SMB_VFS_REMOVEXATTR(handle->conn, base, xattr_name);
481
482         if ((ret == -1) && (errno == ENOATTR)) {
483                 errno = ENOENT;
484                 goto fail;
485         }
486
487         ret = 0;
488
489  fail:
490         TALLOC_FREE(base);
491         TALLOC_FREE(sname);
492         return ret;
493 }
494
495 static int streams_xattr_rename(vfs_handle_struct *handle,
496                                 const char *oldname,
497                                 const char *newname)
498 {
499         NTSTATUS status;
500         TALLOC_CTX *frame = NULL;
501         char *obase;
502         char *ostream;
503         char *nbase;
504         char *nstream;
505         const char *base;
506         int ret = -1;
507         char *oxattr_name;
508         char *nxattr_name;
509         bool o_is_stream;
510         bool n_is_stream;
511         ssize_t oret;
512         ssize_t nret;
513         struct ea_struct ea;
514
515         o_is_stream = is_ntfs_stream_name(oldname);
516         n_is_stream = is_ntfs_stream_name(newname);
517
518         if (!o_is_stream && !n_is_stream) {
519                 return SMB_VFS_NEXT_RENAME(handle, oldname, newname);
520         }
521
522         frame = talloc_stackframe();
523         if (!frame) {
524                 goto fail;
525         }
526
527         status = split_ntfs_stream_name(talloc_tos(), oldname, &obase, &ostream);
528         if (!NT_STATUS_IS_OK(status)) {
529                 errno = EINVAL;
530                 goto fail;
531         }
532
533         status = split_ntfs_stream_name(talloc_tos(), newname, &nbase, &nstream);
534         if (!NT_STATUS_IS_OK(status)) {
535                 errno = EINVAL;
536                 goto fail;
537         }
538
539         /*TODO: maybe call SMB_VFS_NEXT_RENAME() both streams are NULL (::$DATA) */
540         if (ostream == NULL) {
541                 errno = ENOSYS;
542                 goto fail;
543         }
544
545         if (nstream == NULL) {
546                 errno = ENOSYS;
547                 goto fail;
548         }
549
550         if (StrCaseCmp(ostream, nstream) == 0) {
551                 goto done;
552         }
553
554         base = obase;
555
556         oxattr_name = talloc_asprintf(talloc_tos(), "%s%s",
557                                       SAMBA_XATTR_DOSSTREAM_PREFIX, ostream);
558         if (oxattr_name == NULL) {
559                 errno = ENOMEM;
560                 goto fail;
561         }
562
563         nxattr_name = talloc_asprintf(talloc_tos(), "%s%s",
564                                       SAMBA_XATTR_DOSSTREAM_PREFIX, nstream);
565         if (nxattr_name == NULL) {
566                 errno = ENOMEM;
567                 goto fail;
568         }
569
570         /* read the old stream */
571         status = get_ea_value(talloc_tos(), handle->conn, NULL,
572                               base, oxattr_name, &ea);
573         if (!NT_STATUS_IS_OK(status)) {
574                 errno = ENOENT;
575                 goto fail;
576         }
577
578         /* (over)write the new stream */
579         nret = SMB_VFS_SETXATTR(handle->conn, base, nxattr_name,
580                                 ea.value.data, ea.value.length, 0);
581         if (nret < 0) {
582                 if (errno == ENOATTR) {
583                         errno = ENOENT;
584                 }
585                 goto fail;
586         }
587
588         /* remove the old stream */
589         oret = SMB_VFS_REMOVEXATTR(handle->conn, base, oxattr_name);
590         if (oret < 0) {
591                 if (errno == ENOATTR) {
592                         errno = ENOENT;
593                 }
594                 goto fail;
595         }
596
597  done:
598         errno = 0;
599         ret = 0;
600  fail:
601         TALLOC_FREE(frame);
602         return ret;
603 }
604
605 static NTSTATUS walk_xattr_streams(connection_struct *conn, files_struct *fsp,
606                                    const char *fname,
607                                    bool (*fn)(struct ea_struct *ea,
608                                               void *private_data),
609                                    void *private_data)
610 {
611         NTSTATUS status;
612         char **names;
613         size_t i, num_names;
614         size_t prefix_len = strlen(SAMBA_XATTR_DOSSTREAM_PREFIX);
615
616         status = get_ea_names_from_file(talloc_tos(), conn, fsp, fname,
617                                         &names, &num_names);
618         if (!NT_STATUS_IS_OK(status)) {
619                 return status;
620         }
621
622         for (i=0; i<num_names; i++) {
623                 struct ea_struct ea;
624
625                 if (strncmp(names[i], SAMBA_XATTR_DOSSTREAM_PREFIX,
626                             prefix_len) != 0) {
627                         continue;
628                 }
629
630                 status = get_ea_value(names, conn, fsp, fname, names[i], &ea);
631                 if (!NT_STATUS_IS_OK(status)) {
632                         DEBUG(10, ("Could not get ea %s for file %s: %s\n",
633                                    names[i], fname, nt_errstr(status)));
634                         continue;
635                 }
636
637                 ea.name = talloc_asprintf(ea.value.data, ":%s",
638                                           names[i] + prefix_len);
639                 if (ea.name == NULL) {
640                         DEBUG(0, ("talloc failed\n"));
641                         continue;
642                 }
643
644                 if (!fn(&ea, private_data)) {
645                         TALLOC_FREE(ea.value.data);
646                         return NT_STATUS_OK;
647                 }
648
649                 TALLOC_FREE(ea.value.data);
650         }
651
652         TALLOC_FREE(names);
653         return NT_STATUS_OK;
654 }
655
656 static bool add_one_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
657                            struct stream_struct **streams,
658                            const char *name, SMB_OFF_T size,
659                            SMB_OFF_T alloc_size)
660 {
661         struct stream_struct *tmp;
662
663         tmp = TALLOC_REALLOC_ARRAY(mem_ctx, *streams, struct stream_struct,
664                                    (*num_streams)+1);
665         if (tmp == NULL) {
666                 return false;
667         }
668
669         tmp[*num_streams].name = talloc_strdup(tmp, name);
670         if (tmp[*num_streams].name == NULL) {
671                 return false;
672         }
673
674         tmp[*num_streams].size = size;
675         tmp[*num_streams].alloc_size = alloc_size;
676
677         *streams = tmp;
678         *num_streams += 1;
679         return true;
680 }
681
682 struct streaminfo_state {
683         TALLOC_CTX *mem_ctx;
684         vfs_handle_struct *handle;
685         unsigned int num_streams;
686         struct stream_struct *streams;
687         NTSTATUS status;
688 };
689
690 static bool collect_one_stream(struct ea_struct *ea, void *private_data)
691 {
692         struct streaminfo_state *state =
693                 (struct streaminfo_state *)private_data;
694
695         if (!add_one_stream(state->mem_ctx,
696                             &state->num_streams, &state->streams,
697                             ea->name, ea->value.length-1,
698                             smb_roundup(state->handle->conn,
699                                         ea->value.length-1))) {
700                 state->status = NT_STATUS_NO_MEMORY;
701                 return false;
702         }
703
704         return true;
705 }
706
707 static NTSTATUS streams_xattr_streaminfo(vfs_handle_struct *handle,
708                                          struct files_struct *fsp,
709                                          const char *fname,
710                                          TALLOC_CTX *mem_ctx,
711                                          unsigned int *pnum_streams,
712                                          struct stream_struct **pstreams)
713 {
714         SMB_STRUCT_STAT sbuf;
715         int ret;
716         NTSTATUS status;
717         struct streaminfo_state state;
718
719         if ((fsp != NULL) && (fsp->fh->fd != -1)) {
720                 if (is_ntfs_stream_name(fsp->fsp_name)) {
721                         return NT_STATUS_INVALID_PARAMETER;
722                 }
723                 ret = SMB_VFS_FSTAT(fsp, &sbuf);
724         }
725         else {
726                 if (is_ntfs_stream_name(fname)) {
727                         return NT_STATUS_INVALID_PARAMETER;
728                 }
729                 if (lp_posix_pathnames()) {
730                         ret = SMB_VFS_LSTAT(handle->conn, fname, &sbuf);
731                 } else {
732                         ret = SMB_VFS_STAT(handle->conn, fname, &sbuf);
733                 }
734         }
735
736         if (ret == -1) {
737                 return map_nt_error_from_unix(errno);
738         }
739
740         state.streams = NULL;
741         state.num_streams = 0;
742
743         if (!S_ISDIR(sbuf.st_mode)) {
744                 if (!add_one_stream(mem_ctx,
745                                     &state.num_streams, &state.streams,
746                                     "::$DATA", sbuf.st_size,
747                                     SMB_VFS_GET_ALLOC_SIZE(handle->conn, fsp,
748                                                            &sbuf))) {
749                         return NT_STATUS_NO_MEMORY;
750                 }
751         }
752
753         state.mem_ctx = mem_ctx;
754         state.handle = handle;
755         state.status = NT_STATUS_OK;
756
757         status = walk_xattr_streams(handle->conn, fsp, fname,
758                                     collect_one_stream, &state);
759
760         if (!NT_STATUS_IS_OK(status)) {
761                 TALLOC_FREE(state.streams);
762                 return status;
763         }
764
765         if (!NT_STATUS_IS_OK(state.status)) {
766                 TALLOC_FREE(state.streams);
767                 return state.status;
768         }
769
770         *pnum_streams = state.num_streams;
771         *pstreams = state.streams;
772         return NT_STATUS_OK;
773 }
774
775 static uint32_t streams_xattr_fs_capabilities(struct vfs_handle_struct *handle)
776 {
777         return SMB_VFS_NEXT_FS_CAPABILITIES(handle) | FILE_NAMED_STREAMS;
778 }
779
780 static ssize_t streams_xattr_pwrite(vfs_handle_struct *handle,
781                                     files_struct *fsp, const void *data,
782                                     size_t n, SMB_OFF_T offset)
783 {
784         struct stream_io *sio =
785                 (struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
786         struct ea_struct ea;
787         NTSTATUS status;
788         int ret;
789
790         DEBUG(10, ("streams_xattr_pwrite called for %d bytes\n", (int)n));
791
792         if (sio == NULL) {
793                 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
794         }
795
796         if (!streams_xattr_recheck(sio)) {
797                 return -1;
798         }
799
800         status = get_ea_value(talloc_tos(), handle->conn, fsp->base_fsp,
801                               sio->base, sio->xattr_name, &ea);
802         if (!NT_STATUS_IS_OK(status)) {
803                 return -1;
804         }
805
806         if ((offset + n) > ea.value.length-1) {
807                 uint8 *tmp;
808
809                 tmp = TALLOC_REALLOC_ARRAY(talloc_tos(), ea.value.data, uint8,
810                                            offset + n + 1);
811
812                 if (tmp == NULL) {
813                         TALLOC_FREE(ea.value.data);
814                         errno = ENOMEM;
815                         return -1;
816                 }
817                 ea.value.data = tmp;
818                 ea.value.length = offset + n + 1;
819                 ea.value.data[offset+n] = 0;
820         }
821
822         memcpy(ea.value.data + offset, data, n);
823
824         if (fsp->base_fsp->fh->fd != -1) {
825                 ret = SMB_VFS_FSETXATTR(fsp->base_fsp,
826                                 sio->xattr_name,
827                                 ea.value.data, ea.value.length, 0);
828         } else {
829                 ret = SMB_VFS_SETXATTR(fsp->conn, fsp->base_fsp->fsp_name,
830                                 sio->xattr_name,
831                                 ea.value.data, ea.value.length, 0);
832         }
833         TALLOC_FREE(ea.value.data);
834
835         if (ret == -1) {
836                 return -1;
837         }
838
839         return n;
840 }
841
842 static ssize_t streams_xattr_pread(vfs_handle_struct *handle,
843                                    files_struct *fsp, void *data,
844                                    size_t n, SMB_OFF_T offset)
845 {
846         struct stream_io *sio =
847                 (struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
848         struct ea_struct ea;
849         NTSTATUS status;
850         size_t length, overlap;
851
852         if (sio == NULL) {
853                 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
854         }
855
856         if (!streams_xattr_recheck(sio)) {
857                 return -1;
858         }
859
860         status = get_ea_value(talloc_tos(), handle->conn, fsp->base_fsp,
861                               sio->base, sio->xattr_name, &ea);
862         if (!NT_STATUS_IS_OK(status)) {
863                 return -1;
864         }
865
866         length = ea.value.length-1;
867
868         /* Attempt to read past EOF. */
869         if (length <= offset) {
870                 errno = EINVAL;
871                 return -1;
872         }
873
874         overlap = (offset + n) > length ? (length - offset) : n;
875         memcpy(data, ea.value.data + offset, overlap);
876
877         TALLOC_FREE(ea.value.data);
878         return overlap;
879 }
880
881 static int streams_xattr_ftruncate(struct vfs_handle_struct *handle,
882                                         struct files_struct *fsp,
883                                         SMB_OFF_T offset)
884 {
885         int ret;
886         uint8 *tmp;
887         struct ea_struct ea;
888         NTSTATUS status;
889         struct stream_io *sio =
890                 (struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
891
892         DEBUG(10, ("streams_xattr_ftruncate called for file %s offset %.0f\n",
893                 fsp->fsp_name,
894                 (double)offset ));
895
896         if (sio == NULL) {
897                 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
898         }
899
900         if (!streams_xattr_recheck(sio)) {
901                 return -1;
902         }
903
904         status = get_ea_value(talloc_tos(), handle->conn, fsp->base_fsp,
905                               sio->base, sio->xattr_name, &ea);
906         if (!NT_STATUS_IS_OK(status)) {
907                 return -1;
908         }
909
910         tmp = TALLOC_REALLOC_ARRAY(talloc_tos(), ea.value.data, uint8,
911                                    offset + 1);
912
913         if (tmp == NULL) {
914                 TALLOC_FREE(ea.value.data);
915                 errno = ENOMEM;
916                 return -1;
917         }
918
919         /* Did we expand ? */
920         if (ea.value.length < offset + 1) {
921                 memset(&tmp[ea.value.length], '\0',
922                         offset + 1 - ea.value.length);
923         }
924
925         ea.value.data = tmp;
926         ea.value.length = offset + 1;
927         ea.value.data[offset] = 0;
928
929         if (fsp->base_fsp->fh->fd != -1) {
930                 ret = SMB_VFS_FSETXATTR(fsp->base_fsp,
931                                 sio->xattr_name,
932                                 ea.value.data, ea.value.length, 0);
933         } else {
934                 ret = SMB_VFS_SETXATTR(fsp->conn, fsp->base_fsp->fsp_name,
935                                 sio->xattr_name,
936                                 ea.value.data, ea.value.length, 0);
937         }
938
939         TALLOC_FREE(ea.value.data);
940
941         if (ret == -1) {
942                 return -1;
943         }
944
945         return 0;
946 }
947
948 /* VFS operations structure */
949
950 static vfs_op_tuple streams_xattr_ops[] = {
951         {SMB_VFS_OP(streams_xattr_fs_capabilities), SMB_VFS_OP_FS_CAPABILITIES,
952          SMB_VFS_LAYER_TRANSPARENT},
953         {SMB_VFS_OP(streams_xattr_open), SMB_VFS_OP_OPEN,
954          SMB_VFS_LAYER_TRANSPARENT},
955         {SMB_VFS_OP(streams_xattr_stat), SMB_VFS_OP_STAT,
956          SMB_VFS_LAYER_TRANSPARENT},
957         {SMB_VFS_OP(streams_xattr_fstat), SMB_VFS_OP_FSTAT,
958          SMB_VFS_LAYER_TRANSPARENT},
959         {SMB_VFS_OP(streams_xattr_lstat), SMB_VFS_OP_LSTAT,
960          SMB_VFS_LAYER_TRANSPARENT},
961         {SMB_VFS_OP(streams_xattr_pread), SMB_VFS_OP_PREAD,
962          SMB_VFS_LAYER_TRANSPARENT},
963         {SMB_VFS_OP(streams_xattr_pwrite), SMB_VFS_OP_PWRITE,
964          SMB_VFS_LAYER_TRANSPARENT},
965         {SMB_VFS_OP(streams_xattr_unlink), SMB_VFS_OP_UNLINK,
966          SMB_VFS_LAYER_TRANSPARENT},
967         {SMB_VFS_OP(streams_xattr_rename), SMB_VFS_OP_RENAME,
968          SMB_VFS_LAYER_TRANSPARENT},
969         {SMB_VFS_OP(streams_xattr_ftruncate),  SMB_VFS_OP_FTRUNCATE,
970          SMB_VFS_LAYER_TRANSPARENT},
971         {SMB_VFS_OP(streams_xattr_streaminfo), SMB_VFS_OP_STREAMINFO,
972          SMB_VFS_LAYER_OPAQUE},
973         {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
974 };
975
976 NTSTATUS vfs_streams_xattr_init(void);
977 NTSTATUS vfs_streams_xattr_init(void)
978 {
979         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "streams_xattr",
980                                 streams_xattr_ops);
981 }