Add SMB_VFS_FS_CAPABILITIES
[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 #define XATTR_DOSSTREAM_PREFIX "user.DosStream."
30
31 struct stream_io {
32         char *base;
33         char *xattr_name;
34 };
35
36 static SMB_INO_T stream_inode(const SMB_STRUCT_STAT *sbuf, const char *sname)
37 {
38         struct MD5Context ctx;
39         unsigned char hash[16];
40         SMB_INO_T result;
41         char *upper_sname;
42
43         DEBUG(10, ("stream_inode called for %lu/%lu [%s]\n",
44                    (unsigned long)sbuf->st_dev,
45                    (unsigned long)sbuf->st_ino, sname));
46
47         upper_sname = talloc_strdup_upper(talloc_tos(), sname);
48         SMB_ASSERT(upper_sname != NULL);
49
50         MD5Init(&ctx);
51         MD5Update(&ctx, (unsigned char *)&(sbuf->st_dev),
52                   sizeof(sbuf->st_dev));
53         MD5Update(&ctx, (unsigned char *)&(sbuf->st_ino),
54                   sizeof(sbuf->st_ino));
55         MD5Update(&ctx, (unsigned char *)upper_sname,
56                   talloc_get_size(upper_sname)-1);
57         MD5Final(hash, &ctx);
58
59         TALLOC_FREE(upper_sname);
60
61         /* Hopefully all the variation is in the lower 4 (or 8) bytes! */
62         memcpy(&result, hash, sizeof(result));
63
64         DEBUG(10, ("stream_inode returns %lu\n", (unsigned long)result));
65
66         return result;
67 }
68
69 static ssize_t get_xattr_size(connection_struct *conn, const char *fname,
70                               const char *xattr_name)
71 {
72         NTSTATUS status;
73         struct ea_struct ea;
74         ssize_t result;
75
76         status = get_ea_value(talloc_tos(), conn, NULL, fname,
77                               xattr_name, &ea);
78
79         if (!NT_STATUS_IS_OK(status)) {
80                 return -1;
81         }
82
83         result = ea.value.length-1;
84         TALLOC_FREE(ea.value.data);
85         return result;
86 }
87
88
89 static int streams_xattr_fstat(vfs_handle_struct *handle, files_struct *fsp,
90                                SMB_STRUCT_STAT *sbuf)
91 {
92         struct stream_io *io = (struct stream_io *)
93                 VFS_FETCH_FSP_EXTENSION(handle, fsp);
94
95         DEBUG(10, ("streams_xattr_fstat called for %d\n", fsp->fh->fd));
96
97         if (io == NULL) {
98                 return SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
99         }
100
101         if (SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf) == -1) {
102                 return -1;
103         }
104
105         sbuf->st_size = get_xattr_size(handle->conn, io->base, io->xattr_name);
106         if (sbuf->st_size == -1) {
107                 return -1;
108         }
109
110         DEBUG(10, ("sbuf->st_size = %d\n", (int)sbuf->st_size));
111
112         sbuf->st_ino = stream_inode(sbuf, io->xattr_name);
113         sbuf->st_mode &= ~S_IFMT;
114         sbuf->st_mode |= S_IFREG;
115         sbuf->st_blocks = sbuf->st_size % STAT_ST_BLOCKSIZE + 1;
116
117         return 0;
118 }
119
120 static int streams_xattr_stat(vfs_handle_struct *handle, const char *fname,
121                               SMB_STRUCT_STAT *sbuf)
122 {
123         NTSTATUS status;
124         char *base = NULL, *sname = NULL;
125         int result = -1;
126         char *xattr_name;
127
128         if (!is_ntfs_stream_name(fname)) {
129                 return SMB_VFS_NEXT_STAT(handle, fname, sbuf);
130         }
131
132         status = split_ntfs_stream_name(talloc_tos(), fname, &base, &sname);
133         if (!NT_STATUS_IS_OK(status)) {
134                 errno = EINVAL;
135                 return -1;
136         }
137
138         if (SMB_VFS_STAT(handle->conn, base, sbuf) == -1) {
139                 goto fail;
140         }
141
142         xattr_name = talloc_asprintf(talloc_tos(), "%s%s",
143                                      XATTR_DOSSTREAM_PREFIX, sname);
144         if (xattr_name == NULL) {
145                 errno = ENOMEM;
146                 goto fail;
147         }
148
149         sbuf->st_size = get_xattr_size(handle->conn, base, xattr_name);
150         if (sbuf->st_size == -1) {
151                 errno = ENOENT;
152                 goto fail;
153         }
154
155         sbuf->st_ino = stream_inode(sbuf, xattr_name);
156         sbuf->st_mode &= ~S_IFMT;
157         sbuf->st_mode |= S_IFREG;
158         sbuf->st_blocks = sbuf->st_size % STAT_ST_BLOCKSIZE + 1;
159
160         result = 0;
161  fail:
162         TALLOC_FREE(base);
163         TALLOC_FREE(sname);
164         return result;
165 }
166
167 static int streams_xattr_lstat(vfs_handle_struct *handle, const char *fname,
168                                SMB_STRUCT_STAT *sbuf)
169 {
170         NTSTATUS status;
171         char *base, *sname;
172         int result = -1;
173         char *xattr_name;
174
175         if (!is_ntfs_stream_name(fname)) {
176                 return SMB_VFS_NEXT_LSTAT(handle, fname, sbuf);
177         }
178
179         status = split_ntfs_stream_name(talloc_tos(), fname, &base, &sname);
180         if (!NT_STATUS_IS_OK(status)) {
181                 errno = EINVAL;
182                 goto fail;
183         }
184
185         if (SMB_VFS_LSTAT(handle->conn, base, sbuf) == -1) {
186                 goto fail;
187         }
188
189         xattr_name = talloc_asprintf(talloc_tos(), "%s%s",
190                                      XATTR_DOSSTREAM_PREFIX, sname);
191         if (xattr_name == NULL) {
192                 errno = ENOMEM;
193                 goto fail;
194         }
195
196         sbuf->st_size = get_xattr_size(handle->conn, base, xattr_name);
197         if (sbuf->st_size == -1) {
198                 errno = ENOENT;
199                 goto fail;
200         }
201
202         sbuf->st_ino = stream_inode(sbuf, xattr_name);
203         sbuf->st_mode &= ~S_IFMT;
204         sbuf->st_mode |= S_IFREG;
205         sbuf->st_blocks = sbuf->st_size % STAT_ST_BLOCKSIZE + 1;
206
207         result = 0;
208  fail:
209         TALLOC_FREE(base);
210         TALLOC_FREE(sname);
211         return result;
212 }
213
214 static int streams_xattr_open(vfs_handle_struct *handle,  const char *fname,
215                               files_struct *fsp, int flags, mode_t mode)
216 {
217         TALLOC_CTX *frame;
218         NTSTATUS status;
219         struct stream_io *sio;
220         char *base, *sname;
221         struct ea_struct ea;
222         char *xattr_name;
223         int baseflags;
224         int hostfd = -1;
225
226         DEBUG(10, ("streams_xattr_open called for %s\n", fname));
227
228         if (!is_ntfs_stream_name(fname)) {
229                 return SMB_VFS_NEXT_OPEN(handle, fname, fsp, flags, mode);
230         }
231
232         frame = talloc_stackframe();
233
234         status = split_ntfs_stream_name(talloc_tos(), fname,
235                                         &base, &sname);
236         if (!NT_STATUS_IS_OK(status)) {
237                 errno = EINVAL;
238                 goto fail;
239         }
240
241         xattr_name = talloc_asprintf(talloc_tos(), "%s%s",
242                                      XATTR_DOSSTREAM_PREFIX, sname);
243         if (xattr_name == NULL) {
244                 errno = ENOMEM;
245                 goto fail;
246         }
247
248         /*
249          * We use baseflags to turn off nasty side-effects when opening the
250          * underlying file.
251          */
252         baseflags = flags;
253         baseflags &= ~O_TRUNC;
254         baseflags &= ~O_EXCL;
255         baseflags &= ~O_CREAT;
256
257         hostfd = SMB_VFS_OPEN(handle->conn, base, fsp, baseflags, mode);
258
259         /* It is legit to open a stream on a directory, but the base
260          * fd has to be read-only.
261          */
262         if ((hostfd == -1) && (errno == EISDIR)) {
263                 baseflags &= ~O_ACCMODE;
264                 baseflags |= O_RDONLY;
265                 hostfd = SMB_VFS_OPEN(handle->conn, fname, fsp, baseflags,
266                                       mode);
267         }
268
269         if (hostfd == -1) {
270                 goto fail;
271         }
272
273         status = get_ea_value(talloc_tos(), handle->conn, NULL, base,
274                               xattr_name, &ea);
275
276         DEBUG(10, ("get_ea_value returned %s\n", nt_errstr(status)));
277
278         if (!NT_STATUS_IS_OK(status)
279             && !NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
280                 /*
281                  * The base file is not there. This is an error even if we got
282                  * O_CREAT, the higher levels should have created the base
283                  * file for us.
284                  */
285                 DEBUG(10, ("streams_xattr_open: base file %s not around, "
286                            "returning ENOENT\n", base));
287                 errno = ENOENT;
288                 goto fail;
289         }
290
291         if (!NT_STATUS_IS_OK(status)) {
292                 /*
293                  * The attribute does not exist
294                  */
295
296                 if (flags & O_CREAT) {
297                         /*
298                          * Darn, xattrs need at least 1 byte
299                          */
300                         char null = '\0';
301
302                         DEBUG(10, ("creating attribute %s on file %s\n",
303                                    xattr_name, base));
304
305                         if (SMB_VFS_SETXATTR(
306                                 handle->conn, base, xattr_name,
307                                 &null, sizeof(null),
308                                 flags & O_EXCL ? XATTR_CREATE : 0) == -1) {
309                                 goto fail;
310                         }
311                 }
312         }
313
314         if (flags & O_TRUNC) {
315                 char null = '\0';
316                 if (SMB_VFS_SETXATTR(
317                             handle->conn, base, xattr_name,
318                             &null, sizeof(null),
319                             flags & O_EXCL ? XATTR_CREATE : 0) == -1) {
320                         goto fail;
321                 }
322         }
323
324         sio = (struct stream_io *)VFS_ADD_FSP_EXTENSION(handle, fsp,
325                                                         struct stream_io);
326         if (sio == NULL) {
327                 errno = ENOMEM;
328                 goto fail;
329         }
330
331         sio->xattr_name = talloc_strdup(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
332                                         xattr_name);
333         sio->base = talloc_strdup(VFS_MEMCTX_FSP_EXTENSION(handle, fsp),
334                                   base);
335
336         if ((sio->xattr_name == NULL) || (sio->base == NULL)) {
337                 errno = ENOMEM;
338                 goto fail;
339         }
340
341         TALLOC_FREE(frame);
342         return hostfd;
343
344  fail:
345         if (hostfd >= 0) {
346                 /*
347                  * BUGBUGBUG -- we would need to call fd_close_posix here, but
348                  * we don't have a full fsp yet
349                  */
350                 SMB_VFS_CLOSE(fsp, hostfd);
351         }
352
353         TALLOC_FREE(frame);
354         return -1;
355 }
356
357 static int streams_xattr_unlink(vfs_handle_struct *handle,  const char *fname)
358 {
359         NTSTATUS status;
360         char *base = NULL;
361         char *sname = NULL;
362         int ret = -1;
363         char *xattr_name;
364
365         if (!is_ntfs_stream_name(fname)) {
366                 return SMB_VFS_NEXT_UNLINK(handle, fname);
367         }
368
369         status = split_ntfs_stream_name(talloc_tos(), fname, &base, &sname);
370         if (!NT_STATUS_IS_OK(status)) {
371                 errno = EINVAL;
372                 goto fail;
373         }
374
375         xattr_name = talloc_asprintf(talloc_tos(), "%s%s",
376                                      XATTR_DOSSTREAM_PREFIX, sname);
377         if (xattr_name == NULL) {
378                 errno = ENOMEM;
379                 goto fail;
380         }
381
382         ret = SMB_VFS_REMOVEXATTR(handle->conn, base, xattr_name);
383
384         if ((ret == -1) && (errno == ENOATTR)) {
385                 errno = ENOENT;
386                 goto fail;
387         }
388
389         ret = 0;
390
391  fail:
392         TALLOC_FREE(base);
393         TALLOC_FREE(sname);
394         return ret;
395 }
396
397 static NTSTATUS walk_xattr_streams(connection_struct *conn, files_struct *fsp,
398                                    const char *fname,
399                                    bool (*fn)(struct ea_struct *ea,
400                                               void *private_data),
401                                    void *private_data)
402 {
403         NTSTATUS status;
404         char **names;
405         size_t i, num_names;
406         size_t prefix_len = strlen(XATTR_DOSSTREAM_PREFIX);
407
408         status = get_ea_names_from_file(talloc_tos(), conn, fsp, fname,
409                                         &names, &num_names);
410         if (!NT_STATUS_IS_OK(status)) {
411                 return status;
412         }
413
414         for (i=0; i<num_names; i++) {
415                 struct ea_struct ea;
416
417                 if (strncmp(names[i], XATTR_DOSSTREAM_PREFIX,
418                             prefix_len) != 0) {
419                         continue;
420                 }
421
422                 status = get_ea_value(names, conn, fsp, fname, names[i], &ea);
423                 if (!NT_STATUS_IS_OK(status)) {
424                         DEBUG(10, ("Could not get ea %s for file %s: %s\n",
425                                    names[i], fname, nt_errstr(status)));
426                         continue;
427                 }
428
429                 ea.name = talloc_asprintf(ea.value.data, ":%s",
430                                           names[i] + prefix_len);
431                 if (ea.name == NULL) {
432                         DEBUG(0, ("talloc failed\n"));
433                         continue;
434                 }
435
436                 if (!fn(&ea, private_data)) {
437                         TALLOC_FREE(ea.value.data);
438                         return NT_STATUS_OK;
439                 }
440
441                 TALLOC_FREE(ea.value.data);
442         }
443
444         TALLOC_FREE(names);
445         return NT_STATUS_OK;
446 }
447
448 static bool add_one_stream(TALLOC_CTX *mem_ctx, unsigned int *num_streams,
449                            struct stream_struct **streams,
450                            const char *name, SMB_OFF_T size,
451                            SMB_OFF_T alloc_size)
452 {
453         struct stream_struct *tmp;
454
455         tmp = TALLOC_REALLOC_ARRAY(mem_ctx, *streams, struct stream_struct,
456                                    (*num_streams)+1);
457         if (tmp == NULL) {
458                 return false;
459         }
460
461         tmp[*num_streams].name = talloc_strdup(tmp, name);
462         if (tmp[*num_streams].name == NULL) {
463                 return false;
464         }
465
466         tmp[*num_streams].size = size;
467         tmp[*num_streams].alloc_size = alloc_size;
468
469         *streams = tmp;
470         *num_streams += 1;
471         return true;
472 }
473
474 struct streaminfo_state {
475         TALLOC_CTX *mem_ctx;
476         vfs_handle_struct *handle;
477         unsigned int num_streams;
478         struct stream_struct *streams;
479         NTSTATUS status;
480 };
481
482 static bool collect_one_stream(struct ea_struct *ea, void *private_data)
483 {
484         struct streaminfo_state *state =
485                 (struct streaminfo_state *)private_data;
486
487         if (!add_one_stream(state->mem_ctx,
488                             &state->num_streams, &state->streams,
489                             ea->name, ea->value.length-1,
490                             smb_roundup(state->handle->conn,
491                                         ea->value.length-1))) {
492                 state->status = NT_STATUS_NO_MEMORY;
493                 return false;
494         }
495
496         return true;
497 }
498
499 static NTSTATUS streams_xattr_streaminfo(vfs_handle_struct *handle,
500                                          struct files_struct *fsp,
501                                          const char *fname,
502                                          TALLOC_CTX *mem_ctx,
503                                          unsigned int *pnum_streams,
504                                          struct stream_struct **pstreams)
505 {
506         SMB_STRUCT_STAT sbuf;
507         int ret;
508         NTSTATUS status;
509         struct streaminfo_state state;
510
511         if ((fsp != NULL) && (fsp->fh->fd != -1)) {
512                 if (is_ntfs_stream_name(fsp->fsp_name)) {
513                         return NT_STATUS_INVALID_PARAMETER;
514                 }
515                 ret = SMB_VFS_FSTAT(fsp, &sbuf);
516         }
517         else {
518                 if (is_ntfs_stream_name(fname)) {
519                         return NT_STATUS_INVALID_PARAMETER;
520                 }
521                 ret = SMB_VFS_STAT(handle->conn, fname, &sbuf);
522         }
523
524         if (ret == -1) {
525                 return map_nt_error_from_unix(errno);
526         }
527
528         state.streams = NULL;
529         state.num_streams = 0;
530
531         if (!S_ISDIR(sbuf.st_mode)) {
532                 if (!add_one_stream(mem_ctx,
533                                     &state.num_streams, &state.streams,
534                                     "::$DATA", sbuf.st_size,
535                                     get_allocation_size(handle->conn, fsp,
536                                                         &sbuf))) {
537                         return NT_STATUS_NO_MEMORY;
538                 }
539         }
540
541         state.mem_ctx = mem_ctx;
542         state.handle = handle;
543         state.status = NT_STATUS_OK;
544
545         status = walk_xattr_streams(handle->conn, fsp, fname,
546                                     collect_one_stream, &state);
547
548         if (!NT_STATUS_IS_OK(status)) {
549                 TALLOC_FREE(state.streams);
550                 return status;
551         }
552
553         if (!NT_STATUS_IS_OK(state.status)) {
554                 TALLOC_FREE(state.streams);
555                 return state.status;
556         }
557
558         *pnum_streams = state.num_streams;
559         *pstreams = state.streams;
560         return NT_STATUS_OK;
561 }
562
563 static uint32_t streams_xattr_fs_capabilities(struct vfs_handle_struct *handle)
564 {
565         return SMB_VFS_NEXT_FS_CAPABILITIES(handle) | FILE_NAMED_STREAMS;
566 }
567
568 static ssize_t streams_xattr_pwrite(vfs_handle_struct *handle,
569                                     files_struct *fsp, const void *data,
570                                     size_t n, SMB_OFF_T offset)
571 {
572         struct stream_io *sio =
573                 (struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
574         struct ea_struct ea;
575         NTSTATUS status;
576         int ret;
577
578         DEBUG(10, ("streams_xattr_pwrite called for %d bytes\n", (int)n));
579
580         if (sio == NULL) {
581                 return SMB_VFS_NEXT_PWRITE(handle, fsp, data, n, offset);
582         }
583
584         status = get_ea_value(talloc_tos(), handle->conn, fsp->base_fsp,
585                               sio->base, sio->xattr_name, &ea);
586         if (!NT_STATUS_IS_OK(status)) {
587                 return -1;
588         }
589
590         if ((offset + n) > ea.value.length-1) {
591                 uint8 *tmp;
592
593                 tmp = TALLOC_REALLOC_ARRAY(talloc_tos(), ea.value.data, uint8,
594                                            offset + n + 1);
595
596                 if (tmp == NULL) {
597                         TALLOC_FREE(ea.value.data);
598                         errno = ENOMEM;
599                         return -1;
600                 }
601                 ea.value.data = tmp;
602                 ea.value.length = offset + n + 1;
603                 ea.value.data[offset+n] = 0;
604         }
605
606         memcpy(ea.value.data + offset, data, n);
607
608         ret = SMB_VFS_SETXATTR(fsp->conn, fsp->base_fsp->fsp_name,
609                                 sio->xattr_name,
610                                 ea.value.data, ea.value.length, 0);
611
612         TALLOC_FREE(ea.value.data);
613
614         if (ret == -1) {
615                 return -1;
616         }
617
618         return n;
619 }
620
621 static ssize_t streams_xattr_pread(vfs_handle_struct *handle,
622                                    files_struct *fsp, void *data,
623                                    size_t n, SMB_OFF_T offset)
624 {
625         struct stream_io *sio =
626                 (struct stream_io *)VFS_FETCH_FSP_EXTENSION(handle, fsp);
627         struct ea_struct ea;
628         NTSTATUS status;
629         size_t length, overlap;
630
631         if (sio == NULL) {
632                 return SMB_VFS_NEXT_PREAD(handle, fsp, data, n, offset);
633         }
634
635         status = get_ea_value(talloc_tos(), handle->conn, fsp->base_fsp,
636                               sio->base, sio->xattr_name, &ea);
637         if (!NT_STATUS_IS_OK(status)) {
638                 return -1;
639         }
640
641         length = ea.value.length-1;
642
643         /* Attempt to read past EOF. */
644         if (length <= offset) {
645                 errno = EINVAL;
646                 return -1;
647         }
648
649         overlap = (offset + n) > length ? (length - offset) : n;
650         memcpy(data, ea.value.data + offset, overlap);
651
652         TALLOC_FREE(ea.value.data);
653         return overlap;
654 }
655
656 /* VFS operations structure */
657
658 static vfs_op_tuple streams_xattr_ops[] = {
659         {SMB_VFS_OP(streams_xattr_fs_capabilities), SMB_VFS_OP_FS_CAPABILITIES,
660          SMB_VFS_LAYER_TRANSPARENT},
661         {SMB_VFS_OP(streams_xattr_open), SMB_VFS_OP_OPEN,
662          SMB_VFS_LAYER_TRANSPARENT},
663         {SMB_VFS_OP(streams_xattr_stat), SMB_VFS_OP_STAT,
664          SMB_VFS_LAYER_TRANSPARENT},
665         {SMB_VFS_OP(streams_xattr_fstat), SMB_VFS_OP_FSTAT,
666          SMB_VFS_LAYER_TRANSPARENT},
667         {SMB_VFS_OP(streams_xattr_lstat), SMB_VFS_OP_LSTAT,
668          SMB_VFS_LAYER_TRANSPARENT},
669         {SMB_VFS_OP(streams_xattr_pread), SMB_VFS_OP_PREAD,
670          SMB_VFS_LAYER_TRANSPARENT},
671         {SMB_VFS_OP(streams_xattr_pwrite), SMB_VFS_OP_PWRITE,
672          SMB_VFS_LAYER_TRANSPARENT},
673         {SMB_VFS_OP(streams_xattr_lstat), SMB_VFS_OP_LSTAT,
674          SMB_VFS_LAYER_TRANSPARENT},
675         {SMB_VFS_OP(streams_xattr_unlink), SMB_VFS_OP_UNLINK,
676          SMB_VFS_LAYER_TRANSPARENT},
677         {SMB_VFS_OP(streams_xattr_streaminfo), SMB_VFS_OP_STREAMINFO,
678          SMB_VFS_LAYER_OPAQUE},
679         {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
680 };
681
682 NTSTATUS vfs_streams_xattr_init(void);
683 NTSTATUS vfs_streams_xattr_init(void)
684 {
685         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "streams_xattr",
686                                 streams_xattr_ops);
687 }