s3 vfs: Add a destructor to the fsp extension data API
[samba.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         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                                                         NULL);
410         if (sio == NULL) {
411                 errno = ENOMEM;
412                 goto fail;
413         }
414
415         sio->xattr_name = talloc_strdup(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
416                                         xattr_name);
417         sio->base = talloc_strdup(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
418                                   base);
419         sio->fsp_name_ptr = fsp->fsp_name;
420         sio->handle = handle;
421         sio->fsp = fsp;
422
423         if ((sio->xattr_name == NULL) || (sio->base == NULL)) {
424                 errno = ENOMEM;
425                 goto fail;
426         }
427
428         TALLOC_FREE(frame);
429         return hostfd;
430
431  fail:
432         if (hostfd >= 0) {
433                 /*
434                  * BUGBUGBUG -- we would need to call fd_close_posix here, but
435                  * we don't have a full fsp yet
436                  */
437                 SMB_VFS_CLOSE(fsp);
438         }
439
440         TALLOC_FREE(frame);
441         return -1;
442 }
443
444 static int streams_xattr_unlink(vfs_handle_struct *handle,  const char *fname)
445 {
446         NTSTATUS status;
447         char *base = NULL;
448         char *sname = NULL;
449         int ret = -1;
450         char *xattr_name;
451
452         if (!is_ntfs_stream_name(fname)) {
453                 return SMB_VFS_NEXT_UNLINK(handle, fname);
454         }
455
456         status = split_ntfs_stream_name(talloc_tos(), fname, &base, &sname);
457         if (!NT_STATUS_IS_OK(status)) {
458                 errno = EINVAL;
459                 goto fail;
460         }
461
462         if (sname == NULL){
463                 return SMB_VFS_NEXT_UNLINK(handle, base);
464         }
465
466         xattr_name = talloc_asprintf(talloc_tos(), "%s%s",
467                                      SAMBA_XATTR_DOSSTREAM_PREFIX, sname);
468         if (xattr_name == NULL) {
469                 errno = ENOMEM;
470                 goto fail;
471         }
472
473         ret = SMB_VFS_REMOVEXATTR(handle->conn, base, xattr_name);
474
475         if ((ret == -1) && (errno == ENOATTR)) {
476                 errno = ENOENT;
477                 goto fail;
478         }
479
480         ret = 0;
481
482  fail:
483         TALLOC_FREE(base);
484         TALLOC_FREE(sname);
485         return ret;
486 }
487
488 static int streams_xattr_rename(vfs_handle_struct *handle,
489                                 const char *oldname,
490                                 const char *newname)
491 {
492         NTSTATUS status;
493         TALLOC_CTX *frame = NULL;
494         char *obase;
495         char *ostream;
496         char *nbase;
497         char *nstream;
498         const char *base;
499         int ret = -1;
500         char *oxattr_name;
501         char *nxattr_name;
502         bool o_is_stream;
503         bool n_is_stream;
504         ssize_t oret;
505         ssize_t nret;
506         struct ea_struct ea;
507
508         o_is_stream = is_ntfs_stream_name(oldname);
509         n_is_stream = is_ntfs_stream_name(newname);
510
511         if (!o_is_stream && !n_is_stream) {
512                 return SMB_VFS_NEXT_RENAME(handle, oldname, newname);
513         }
514
515         frame = talloc_stackframe();
516         if (!frame) {
517                 goto fail;
518         }
519
520         status = split_ntfs_stream_name(talloc_tos(), oldname, &obase, &ostream);
521         if (!NT_STATUS_IS_OK(status)) {
522                 errno = EINVAL;
523                 goto fail;
524         }
525
526         status = split_ntfs_stream_name(talloc_tos(), newname, &nbase, &nstream);
527         if (!NT_STATUS_IS_OK(status)) {
528                 errno = EINVAL;
529                 goto fail;
530         }
531
532         /*TODO: maybe call SMB_VFS_NEXT_RENAME() both streams are NULL (::$DATA) */
533         if (ostream == NULL) {
534                 errno = ENOSYS;
535                 goto fail;
536         }
537
538         if (nstream == NULL) {
539                 errno = ENOSYS;
540                 goto fail;
541         }
542
543         if (StrCaseCmp(ostream, nstream) == 0) {
544                 goto done;
545         }
546
547         base = obase;
548
549         oxattr_name = talloc_asprintf(talloc_tos(), "%s%s",
550                                       SAMBA_XATTR_DOSSTREAM_PREFIX, ostream);
551         if (oxattr_name == NULL) {
552                 errno = ENOMEM;
553                 goto fail;
554         }
555
556         nxattr_name = talloc_asprintf(talloc_tos(), "%s%s",
557                                       SAMBA_XATTR_DOSSTREAM_PREFIX, nstream);
558         if (nxattr_name == NULL) {
559                 errno = ENOMEM;
560                 goto fail;
561         }
562
563         /* read the old stream */
564         status = get_ea_value(talloc_tos(), handle->conn, NULL,
565                               base, oxattr_name, &ea);
566         if (!NT_STATUS_IS_OK(status)) {
567                 errno = ENOENT;
568                 goto fail;
569         }
570
571         /* (over)write the new stream */
572         nret = SMB_VFS_SETXATTR(handle->conn, base, nxattr_name,
573                                 ea.value.data, ea.value.length, 0);
574         if (nret < 0) {
575                 if (errno == ENOATTR) {
576                         errno = ENOENT;
577                 }
578                 goto fail;
579         }
580
581         /* remove the old stream */
582         oret = SMB_VFS_REMOVEXATTR(handle->conn, base, oxattr_name);
583         if (oret < 0) {
584                 if (errno == ENOATTR) {
585                         errno = ENOENT;
586                 }
587                 goto fail;
588         }
589
590  done:
591         errno = 0;
592         ret = 0;
593  fail:
594         TALLOC_FREE(frame);
595         return ret;
596 }
597
598 static NTSTATUS walk_xattr_streams(connection_struct *conn, files_struct *fsp,
599                                    const char *fname,
600                                    bool (*fn)(struct ea_struct *ea,
601                                               void *private_data),
602                                    void *private_data)
603 {
604         NTSTATUS status;
605         char **names;
606         size_t i, num_names;
607         size_t prefix_len = strlen(SAMBA_XATTR_DOSSTREAM_PREFIX);
608
609         status = get_ea_names_from_file(talloc_tos(), conn, fsp, fname,
610                                         &names, &num_names);
611         if (!NT_STATUS_IS_OK(status)) {
612                 return status;
613         }
614
615         for (i=0; i<num_names; i++) {
616                 struct ea_struct ea;
617
618                 if (strncmp(names[i], SAMBA_XATTR_DOSSTREAM_PREFIX,
619                             prefix_len) != 0) {
620                         continue;
621                 }
622
623                 status = get_ea_value(names, conn, fsp, fname, names[i], &ea);
624                 if (!NT_STATUS_IS_OK(status)) {
625                         DEBUG(10, ("Could not get ea %s for file %s: %s\n",
626                                    names[i], fname, nt_errstr(status)));
627                         continue;
628                 }
629
630                 ea.name = talloc_asprintf(ea.value.data, ":%s",
631                                           names[i] + prefix_len);
632                 if (ea.name == NULL) {
633                         DEBUG(0, ("talloc failed\n"));
634                         continue;
635                 }
636
637                 if (!fn(&ea, private_data)) {
638                         TALLOC_FREE(ea.value.data);
639                         return NT_STATUS_OK;
640                 }
641
642                 TALLOC_FREE(ea.value.data);
643         }
644
645         TALLOC_FREE(names);
646         return NT_STATUS_OK;
647 }
648
649 static bool add_one_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
650                            struct stream_struct **streams,
651                            const char *name, SMB_OFF_T size,
652                            SMB_OFF_T alloc_size)
653 {
654         struct stream_struct *tmp;
655
656         tmp = TALLOC_REALLOC_ARRAY(mem_ctx, *streams, struct stream_struct,
657                                    (*num_streams)+1);
658         if (tmp == NULL) {
659                 return false;
660         }
661
662         tmp[*num_streams].name = talloc_strdup(tmp, name);
663         if (tmp[*num_streams].name == NULL) {
664                 return false;
665         }
666
667         tmp[*num_streams].size = size;
668         tmp[*num_streams].alloc_size = alloc_size;
669
670         *streams = tmp;
671         *num_streams += 1;
672         return true;
673 }
674
675 struct streaminfo_state {
676         TALLOC_CTX *mem_ctx;
677         vfs_handle_struct *handle;
678         unsigned int num_streams;
679         struct stream_struct *streams;
680         NTSTATUS status;
681 };
682
683 static bool collect_one_stream(struct ea_struct *ea, void *private_data)
684 {
685         struct streaminfo_state *state =
686                 (struct streaminfo_state *)private_data;
687
688         if (!add_one_stream(state->mem_ctx,
689                             &state->num_streams, &state->streams,
690                             ea->name, ea->value.length-1,
691                             smb_roundup(state->handle->conn,
692                                         ea->value.length-1))) {
693                 state->status = NT_STATUS_NO_MEMORY;
694                 return false;
695         }
696
697         return true;
698 }
699
700 static NTSTATUS streams_xattr_streaminfo(vfs_handle_struct *handle,
701                                          struct files_struct *fsp,
702                                          const char *fname,
703                                          TALLOC_CTX *mem_ctx,
704                                          unsigned int *pnum_streams,
705                                          struct stream_struct **pstreams)
706 {
707         SMB_STRUCT_STAT sbuf;
708         int ret;
709         NTSTATUS status;
710         struct streaminfo_state state;
711
712         if ((fsp != NULL) && (fsp->fh->fd != -1)) {
713                 if (is_ntfs_stream_name(fsp->fsp_name)) {
714                         return NT_STATUS_INVALID_PARAMETER;
715                 }
716                 ret = SMB_VFS_FSTAT(fsp, &sbuf);
717         }
718         else {
719                 if (is_ntfs_stream_name(fname)) {
720                         return NT_STATUS_INVALID_PARAMETER;
721                 }
722                 ret = SMB_VFS_STAT(handle->conn, fname, &sbuf);
723         }
724
725         if (ret == -1) {
726                 return map_nt_error_from_unix(errno);
727         }
728
729         state.streams = NULL;
730         state.num_streams = 0;
731
732         if (!S_ISDIR(sbuf.st_mode)) {
733                 if (!add_one_stream(mem_ctx,
734                                     &state.num_streams, &state.streams,
735                                     "::$DATA", sbuf.st_size,
736                                     SMB_VFS_GET_ALLOC_SIZE(handle->conn, fsp,
737                                                            &sbuf))) {
738                         return NT_STATUS_NO_MEMORY;
739                 }
740         }
741
742         state.mem_ctx = mem_ctx;
743         state.handle = handle;
744         state.status = NT_STATUS_OK;
745
746         status = walk_xattr_streams(handle->conn, fsp, fname,
747                                     collect_one_stream, &state);
748
749         if (!NT_STATUS_IS_OK(status)) {
750                 TALLOC_FREE(state.streams);
751                 return status;
752         }
753
754         if (!NT_STATUS_IS_OK(state.status)) {
755                 TALLOC_FREE(state.streams);
756                 return state.status;
757         }
758
759         *pnum_streams = state.num_streams;
760         *pstreams = state.streams;
761         return NT_STATUS_OK;
762 }
763
764 static uint32_t streams_xattr_fs_capabilities(struct vfs_handle_struct *handle)
765 {
766         return SMB_VFS_NEXT_FS_CAPABILITIES(handle) | FILE_NAMED_STREAMS;
767 }
768
769 static ssize_t streams_xattr_pwrite(vfs_handle_struct *handle,
770                                     files_struct *fsp, const void *data,
771                                     size_t n, SMB_OFF_T offset)
772 {
773         struct stream_io *sio =
774                 (struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
775         struct ea_struct ea;
776         NTSTATUS status;
777         int ret;
778
779         DEBUG(10, ("streams_xattr_pwrite called for %d bytes\n", (int)n));
780
781         if (sio == NULL) {
782                 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
783         }
784
785         if (!streams_xattr_recheck(sio)) {
786                 return -1;
787         }
788
789         status = get_ea_value(talloc_tos(), handle->conn, fsp->base_fsp,
790                               sio->base, sio->xattr_name, &ea);
791         if (!NT_STATUS_IS_OK(status)) {
792                 return -1;
793         }
794
795         if ((offset + n) > ea.value.length-1) {
796                 uint8 *tmp;
797
798                 tmp = TALLOC_REALLOC_ARRAY(talloc_tos(), ea.value.data, uint8,
799                                            offset + n + 1);
800
801                 if (tmp == NULL) {
802                         TALLOC_FREE(ea.value.data);
803                         errno = ENOMEM;
804                         return -1;
805                 }
806                 ea.value.data = tmp;
807                 ea.value.length = offset + n + 1;
808                 ea.value.data[offset+n] = 0;
809         }
810
811         memcpy(ea.value.data + offset, data, n);
812
813         if (fsp->base_fsp->fh->fd != -1) {
814                 ret = SMB_VFS_FSETXATTR(fsp->base_fsp,
815                                 sio->xattr_name,
816                                 ea.value.data, ea.value.length, 0);
817         } else {
818                 ret = SMB_VFS_SETXATTR(fsp->conn, fsp->base_fsp->fsp_name,
819                                 sio->xattr_name,
820                                 ea.value.data, ea.value.length, 0);
821         }
822         TALLOC_FREE(ea.value.data);
823
824         if (ret == -1) {
825                 return -1;
826         }
827
828         return n;
829 }
830
831 static ssize_t streams_xattr_pread(vfs_handle_struct *handle,
832                                    files_struct *fsp, void *data,
833                                    size_t n, SMB_OFF_T offset)
834 {
835         struct stream_io *sio =
836                 (struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
837         struct ea_struct ea;
838         NTSTATUS status;
839         size_t length, overlap;
840
841         if (sio == NULL) {
842                 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
843         }
844
845         if (!streams_xattr_recheck(sio)) {
846                 return -1;
847         }
848
849         status = get_ea_value(talloc_tos(), handle->conn, fsp->base_fsp,
850                               sio->base, sio->xattr_name, &ea);
851         if (!NT_STATUS_IS_OK(status)) {
852                 return -1;
853         }
854
855         length = ea.value.length-1;
856
857         /* Attempt to read past EOF. */
858         if (length <= offset) {
859                 errno = EINVAL;
860                 return -1;
861         }
862
863         overlap = (offset + n) > length ? (length - offset) : n;
864         memcpy(data, ea.value.data + offset, overlap);
865
866         TALLOC_FREE(ea.value.data);
867         return overlap;
868 }
869
870 static int streams_xattr_ftruncate(struct vfs_handle_struct *handle,
871                                         struct files_struct *fsp,
872                                         SMB_OFF_T offset)
873 {
874         int ret;
875         uint8 *tmp;
876         struct ea_struct ea;
877         NTSTATUS status;
878         struct stream_io *sio =
879                 (struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
880
881         DEBUG(10, ("streams_xattr_ftruncate called for file %s offset %.0f\n",
882                 fsp->fsp_name,
883                 (double)offset ));
884
885         if (sio == NULL) {
886                 return SMB_VFS_NEXT_FTRUNCATE(handle, fsp, offset);
887         }
888
889         if (!streams_xattr_recheck(sio)) {
890                 return -1;
891         }
892
893         status = get_ea_value(talloc_tos(), handle->conn, fsp->base_fsp,
894                               sio->base, sio->xattr_name, &ea);
895         if (!NT_STATUS_IS_OK(status)) {
896                 return -1;
897         }
898
899         tmp = TALLOC_REALLOC_ARRAY(talloc_tos(), ea.value.data, uint8,
900                                    offset + 1);
901
902         if (tmp == NULL) {
903                 TALLOC_FREE(ea.value.data);
904                 errno = ENOMEM;
905                 return -1;
906         }
907
908         /* Did we expand ? */
909         if (ea.value.length < offset + 1) {
910                 memset(&tmp[ea.value.length], '\0',
911                         offset + 1 - ea.value.length);
912         }
913
914         ea.value.data = tmp;
915         ea.value.length = offset + 1;
916         ea.value.data[offset] = 0;
917
918         if (fsp->base_fsp->fh->fd != -1) {
919                 ret = SMB_VFS_FSETXATTR(fsp->base_fsp,
920                                 sio->xattr_name,
921                                 ea.value.data, ea.value.length, 0);
922         } else {
923                 ret = SMB_VFS_SETXATTR(fsp->conn, fsp->base_fsp->fsp_name,
924                                 sio->xattr_name,
925                                 ea.value.data, ea.value.length, 0);
926         }
927
928         TALLOC_FREE(ea.value.data);
929
930         if (ret == -1) {
931                 return -1;
932         }
933
934         return 0;
935 }
936
937 /* VFS operations structure */
938
939 static vfs_op_tuple streams_xattr_ops[] = {
940         {SMB_VFS_OP(streams_xattr_fs_capabilities), SMB_VFS_OP_FS_CAPABILITIES,
941          SMB_VFS_LAYER_TRANSPARENT},
942         {SMB_VFS_OP(streams_xattr_open), SMB_VFS_OP_OPEN,
943          SMB_VFS_LAYER_TRANSPARENT},
944         {SMB_VFS_OP(streams_xattr_stat), SMB_VFS_OP_STAT,
945          SMB_VFS_LAYER_TRANSPARENT},
946         {SMB_VFS_OP(streams_xattr_fstat), SMB_VFS_OP_FSTAT,
947          SMB_VFS_LAYER_TRANSPARENT},
948         {SMB_VFS_OP(streams_xattr_lstat), SMB_VFS_OP_LSTAT,
949          SMB_VFS_LAYER_TRANSPARENT},
950         {SMB_VFS_OP(streams_xattr_pread), SMB_VFS_OP_PREAD,
951          SMB_VFS_LAYER_TRANSPARENT},
952         {SMB_VFS_OP(streams_xattr_pwrite), SMB_VFS_OP_PWRITE,
953          SMB_VFS_LAYER_TRANSPARENT},
954         {SMB_VFS_OP(streams_xattr_unlink), SMB_VFS_OP_UNLINK,
955          SMB_VFS_LAYER_TRANSPARENT},
956         {SMB_VFS_OP(streams_xattr_rename), SMB_VFS_OP_RENAME,
957          SMB_VFS_LAYER_TRANSPARENT},
958         {SMB_VFS_OP(streams_xattr_ftruncate),  SMB_VFS_OP_FTRUNCATE,
959          SMB_VFS_LAYER_TRANSPARENT},
960         {SMB_VFS_OP(streams_xattr_streaminfo), SMB_VFS_OP_STREAMINFO,
961          SMB_VFS_LAYER_OPAQUE},
962         {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
963 };
964
965 NTSTATUS vfs_streams_xattr_init(void);
966 NTSTATUS vfs_streams_xattr_init(void)
967 {
968         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "streams_xattr",
969                                 streams_xattr_ops);
970 }