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