r3798: added support for alternate data streams in xattrs into pvfs.
[jra/samba/.git] / source4 / ntvfs / posix / pvfs_xattr.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    POSIX NTVFS backend - xattr support
5
6    Copyright (C) Andrew Tridgell 2004
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24 #include "system/filesys.h"
25 #include "vfs_posix.h"
26 #include "librpc/gen_ndr/ndr_xattr.h"
27
28 /*
29   pull a xattr as a blob, from either a file or a file descriptor
30 */
31 static NTSTATUS pull_xattr_blob(struct pvfs_state *pvfs,
32                                 TALLOC_CTX *mem_ctx,
33                                 const char *attr_name, 
34                                 const char *fname, 
35                                 int fd, 
36                                 size_t estimated_size,
37                                 DATA_BLOB *blob)
38 {
39 #if HAVE_XATTR_SUPPORT
40         int ret;
41
42         *blob = data_blob_talloc(mem_ctx, NULL, estimated_size);
43         if (blob->data == NULL) {
44                 return NT_STATUS_NO_MEMORY;
45         }
46
47 again:
48         if (fd != -1) {
49                 ret = fgetxattr(fd, attr_name, blob->data, estimated_size);
50         } else {
51                 ret = getxattr(fname, attr_name, blob->data, estimated_size);
52         }
53         if (ret == -1 && errno == ERANGE) {
54                 estimated_size *= 2;
55                 blob->data = talloc_realloc(mem_ctx, blob->data, estimated_size);
56                 if (blob->data == NULL) {
57                         return NT_STATUS_NO_MEMORY;
58                 }
59                 blob->length = estimated_size;
60                 goto again;
61         }
62
63         if (ret == -1) {
64                 data_blob_free(blob);
65                 return pvfs_map_errno(pvfs, errno);
66         }
67
68         blob->length = ret;
69
70         return NT_STATUS_OK;
71 #else
72         return NT_STATUS_NOT_SUPPORTED;
73 #endif
74 }
75
76 /*
77   push a xattr as a blob, from either a file or a file descriptor
78 */
79 static NTSTATUS push_xattr_blob(struct pvfs_state *pvfs,
80                                 const char *attr_name, 
81                                 const char *fname, 
82                                 int fd, 
83                                 const DATA_BLOB *blob)
84 {
85 #if HAVE_XATTR_SUPPORT
86         int ret;
87
88         if (fd != -1) {
89                 ret = fsetxattr(fd, attr_name, blob->data, blob->length, 0);
90         } else {
91                 ret = setxattr(fname, attr_name, blob->data, blob->length, 0);
92         }
93         if (ret == -1) {
94                 return pvfs_map_errno(pvfs, errno);
95         }
96
97         return NT_STATUS_OK;
98 #else
99         return NT_STATUS_NOT_SUPPORTED;
100 #endif
101 }
102
103
104 /*
105   delete a xattr
106 */
107 static NTSTATUS delete_xattr(struct pvfs_state *pvfs, const char *attr_name, 
108                              const char *fname, int fd)
109 {
110 #if HAVE_XATTR_SUPPORT
111         int ret;
112
113         if (fd != -1) {
114                 ret = fremovexattr(fd, attr_name);
115         } else {
116                 ret = removexattr(fname, attr_name);
117         }
118         if (ret == -1) {
119                 return pvfs_map_errno(pvfs, errno);
120         }
121
122         return NT_STATUS_OK;
123 #else
124         return NT_STATUS_NOT_SUPPORTED;
125 #endif
126 }
127
128 /*
129   load a NDR structure from a xattr
130 */
131 static NTSTATUS pvfs_xattr_ndr_load(struct pvfs_state *pvfs,
132                                     TALLOC_CTX *mem_ctx,
133                                     const char *fname, int fd, const char *attr_name,
134                                     void *p, ndr_pull_flags_fn_t pull_fn)
135 {
136         NTSTATUS status;
137         DATA_BLOB blob;
138
139         status = pull_xattr_blob(pvfs, mem_ctx, attr_name, fname, 
140                                  fd, XATTR_DOSATTRIB_ESTIMATED_SIZE, &blob);
141         if (!NT_STATUS_IS_OK(status)) {
142                 return status;
143         }
144
145         /* pull the blob */
146         status = ndr_pull_struct_blob(&blob, mem_ctx, p, pull_fn);
147
148         data_blob_free(&blob);
149
150         return status;
151 }
152
153 /*
154   save a NDR structure into a xattr
155 */
156 static NTSTATUS pvfs_xattr_ndr_save(struct pvfs_state *pvfs,
157                                     const char *fname, int fd, const char *attr_name, 
158                                     void *p, ndr_push_flags_fn_t push_fn)
159 {
160         TALLOC_CTX *mem_ctx = talloc(NULL, 0);
161         DATA_BLOB blob;
162         NTSTATUS status;
163
164         status = ndr_push_struct_blob(&blob, mem_ctx, p, (ndr_push_flags_fn_t)push_fn);
165         if (!NT_STATUS_IS_OK(status)) {
166                 talloc_free(mem_ctx);
167                 return status;
168         }
169
170         status = push_xattr_blob(pvfs, attr_name, fname, fd, &blob);
171         talloc_free(mem_ctx);
172
173         return status;
174 }
175
176
177 /*
178   fill in file attributes from extended attributes
179 */
180 NTSTATUS pvfs_dosattrib_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd)
181 {
182         NTSTATUS status;
183         struct xattr_DosAttrib attrib;
184         TALLOC_CTX *mem_ctx = talloc(name, 0);
185         struct xattr_DosInfo1 *info1;
186
187         if (name->stream_name != NULL) {
188                 name->stream_exists = False;
189         } else {
190                 name->stream_exists = True;
191         }
192
193         if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
194                 return NT_STATUS_OK;
195         }
196
197         status = pvfs_xattr_ndr_load(pvfs, mem_ctx, name->full_name, 
198                                      fd, XATTR_DOSATTRIB_NAME,
199                                      &attrib, 
200                                      (ndr_pull_flags_fn_t)ndr_pull_xattr_DosAttrib);
201
202         /* if the filesystem doesn't support them, then tell pvfs not to try again */
203         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
204                 DEBUG(5,("pvfs_xattr: xattr not supported in filesystem\n"));
205                 pvfs->flags &= ~PVFS_FLAG_XATTR_ENABLE;
206                 talloc_free(mem_ctx);
207                 return NT_STATUS_OK;
208         }
209
210         /* not having a DosAttrib is not an error */
211         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
212                 talloc_free(mem_ctx);
213                 return NT_STATUS_OK;
214         }
215
216         if (!NT_STATUS_IS_OK(status)) {
217                 talloc_free(mem_ctx);
218                 return status;
219         }
220
221         switch (attrib.version) {
222         case 1:
223                 info1 = &attrib.info.info1;
224                 name->dos.attrib = pvfs_attrib_normalise(info1->attrib);
225                 name->dos.ea_size = info1->ea_size;
226                 if (name->st.st_size == info1->size) {
227                         name->dos.alloc_size = info1->alloc_size;
228                 }
229                 if (info1->create_time != 0) {
230                         name->dos.create_time = info1->create_time;
231                 }
232                 if (info1->change_time != 0) {
233                         name->dos.change_time = info1->change_time;
234                 }
235                 break;
236
237         default:
238                 DEBUG(0,("ERROR: Unsupported xattr DosAttrib version %d on '%s'\n",
239                          attrib.version, name->full_name));
240                 talloc_free(mem_ctx);
241                 return NT_STATUS_INVALID_LEVEL;
242         }
243         talloc_free(mem_ctx);
244         
245         status = pvfs_stream_info(pvfs, name, fd);
246
247         return status;
248 }
249
250
251 /*
252   save the file attribute into the xattr
253 */
254 NTSTATUS pvfs_dosattrib_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd)
255 {
256         struct xattr_DosAttrib attrib;
257         struct xattr_DosInfo1 *info1;
258
259         if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
260                 return NT_STATUS_OK;
261         }
262
263         attrib.version = 1;
264         info1 = &attrib.info.info1;
265
266         name->dos.attrib = pvfs_attrib_normalise(name->dos.attrib);
267
268         info1->attrib      = name->dos.attrib;
269         info1->ea_size     = name->dos.ea_size;
270         info1->size        = name->st.st_size;
271         info1->alloc_size  = name->dos.alloc_size;
272         info1->create_time = name->dos.create_time;
273         info1->change_time = name->dos.change_time;
274
275         return pvfs_xattr_ndr_save(pvfs, name->full_name, fd, 
276                                    XATTR_DOSATTRIB_NAME, &attrib, 
277                                    (ndr_push_flags_fn_t)ndr_push_xattr_DosAttrib);
278 }
279
280
281 /*
282   load the set of DOS EAs
283 */
284 NTSTATUS pvfs_doseas_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
285                           struct xattr_DosEAs *eas)
286 {
287         NTSTATUS status;
288         ZERO_STRUCTP(eas);
289         if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
290                 return NT_STATUS_OK;
291         }
292         status = pvfs_xattr_ndr_load(pvfs, eas, name->full_name, fd, XATTR_DOSEAS_NAME,
293                                      eas, (ndr_pull_flags_fn_t)ndr_pull_xattr_DosEAs);
294         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
295                 return NT_STATUS_OK;
296         }
297         return status;
298 }
299
300 /*
301   save the set of DOS EAs
302 */
303 NTSTATUS pvfs_doseas_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
304                           struct xattr_DosEAs *eas)
305 {
306         if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
307                 return NT_STATUS_OK;
308         }
309         return pvfs_xattr_ndr_save(pvfs, name->full_name, fd, XATTR_DOSEAS_NAME, eas, 
310                                    (ndr_push_flags_fn_t)ndr_push_xattr_DosEAs);
311 }
312
313
314 /*
315   load the set of streams from extended attributes
316 */
317 NTSTATUS pvfs_streams_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
318                            struct xattr_DosStreams *streams)
319 {
320         NTSTATUS status;
321         ZERO_STRUCTP(streams);
322         if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
323                 return NT_STATUS_OK;
324         }
325         status = pvfs_xattr_ndr_load(pvfs, streams, name->full_name, fd, 
326                                      XATTR_DOSSTREAMS_NAME,
327                                      streams, 
328                                      (ndr_pull_flags_fn_t)ndr_pull_xattr_DosStreams);
329         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
330                 return NT_STATUS_OK;
331         }
332         return status;
333 }
334
335 /*
336   save the set of streams into filesystem xattr
337 */
338 NTSTATUS pvfs_streams_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
339                            struct xattr_DosStreams *streams)
340 {
341         if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
342                 return NT_STATUS_OK;
343         }
344         return pvfs_xattr_ndr_save(pvfs, name->full_name, fd, 
345                                    XATTR_DOSSTREAMS_NAME, 
346                                    streams, 
347                                    (ndr_push_flags_fn_t)ndr_push_xattr_DosStreams);
348 }
349
350 /*
351   create a zero length xattr with the given name
352 */
353 NTSTATUS pvfs_xattr_create(struct pvfs_state *pvfs, 
354                            const char *fname, int fd,
355                            const char *attr_prefix,
356                            const char *attr_name)
357 {
358         NTSTATUS status;
359         DATA_BLOB blob = data_blob(NULL, 0);
360         char *aname = talloc_asprintf(NULL, "%s%s", attr_prefix, attr_name);
361         if (aname == NULL) {
362                 return NT_STATUS_NO_MEMORY;
363         }
364         status = push_xattr_blob(pvfs, aname, fname, fd, &blob);
365         talloc_free(aname);
366         return status;
367 }
368
369
370 /*
371   delete a xattr with the given name
372 */
373 NTSTATUS pvfs_xattr_delete(struct pvfs_state *pvfs, 
374                            const char *fname, int fd,
375                            const char *attr_prefix,
376                            const char *attr_name)
377 {
378         NTSTATUS status;
379         char *aname = talloc_asprintf(NULL, "%s%s", attr_prefix, attr_name);
380         if (aname == NULL) {
381                 return NT_STATUS_NO_MEMORY;
382         }
383         status = delete_xattr(pvfs, aname, fname, fd);
384         talloc_free(aname);
385         return status;
386 }
387
388 /*
389   load a xattr with the given name
390 */
391 NTSTATUS pvfs_xattr_load(struct pvfs_state *pvfs, 
392                          TALLOC_CTX *mem_ctx,
393                          const char *fname, int fd,
394                          const char *attr_prefix,
395                          const char *attr_name,
396                          size_t estimated_size,
397                          DATA_BLOB *blob)
398 {
399         NTSTATUS status;
400         char *aname = talloc_asprintf(mem_ctx, "%s%s", attr_prefix, attr_name);
401         if (aname == NULL) {
402                 return NT_STATUS_NO_MEMORY;
403         }
404         status = pull_xattr_blob(pvfs, mem_ctx, aname, fname, fd, estimated_size, blob);
405         talloc_free(aname);
406         return status;
407 }
408
409 /*
410   save a xattr with the given name
411 */
412 NTSTATUS pvfs_xattr_save(struct pvfs_state *pvfs, 
413                          const char *fname, int fd,
414                          const char *attr_prefix,
415                          const char *attr_name,
416                          const DATA_BLOB *blob)
417 {
418         NTSTATUS status;
419         char *aname = talloc_asprintf(NULL, "%s%s", attr_prefix, attr_name);
420         if (aname == NULL) {
421                 return NT_STATUS_NO_MEMORY;
422         }
423         status = push_xattr_blob(pvfs, aname, fname, fd, blob);
424         talloc_free(aname);
425         return status;
426 }