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