r3834: - fixed XATTR_NTACL_NAME
[nivanova/samba-autobuild/.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 = 
228                                 pvfs_round_alloc_size(pvfs, info1->alloc_size);
229                 }
230                 if (info1->create_time != 0) {
231                         name->dos.create_time = info1->create_time;
232                 }
233                 if (info1->change_time != 0) {
234                         name->dos.change_time = info1->change_time;
235                 }
236                 break;
237
238         default:
239                 DEBUG(0,("ERROR: Unsupported xattr DosAttrib version %d on '%s'\n",
240                          attrib.version, name->full_name));
241                 talloc_free(mem_ctx);
242                 return NT_STATUS_INVALID_LEVEL;
243         }
244         talloc_free(mem_ctx);
245         
246         status = pvfs_stream_info(pvfs, name, fd);
247
248         return status;
249 }
250
251
252 /*
253   save the file attribute into the xattr
254 */
255 NTSTATUS pvfs_dosattrib_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd)
256 {
257         struct xattr_DosAttrib attrib;
258         struct xattr_DosInfo1 *info1;
259
260         if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
261                 return NT_STATUS_OK;
262         }
263
264         attrib.version = 1;
265         info1 = &attrib.info.info1;
266
267         name->dos.attrib = pvfs_attrib_normalise(name->dos.attrib);
268
269         info1->attrib      = name->dos.attrib;
270         info1->ea_size     = name->dos.ea_size;
271         info1->size        = name->st.st_size;
272         info1->alloc_size  = name->dos.alloc_size;
273         info1->create_time = name->dos.create_time;
274         info1->change_time = name->dos.change_time;
275
276         return pvfs_xattr_ndr_save(pvfs, name->full_name, fd, 
277                                    XATTR_DOSATTRIB_NAME, &attrib, 
278                                    (ndr_push_flags_fn_t)ndr_push_xattr_DosAttrib);
279 }
280
281
282 /*
283   load the set of DOS EAs
284 */
285 NTSTATUS pvfs_doseas_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
286                           struct xattr_DosEAs *eas)
287 {
288         NTSTATUS status;
289         ZERO_STRUCTP(eas);
290         if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
291                 return NT_STATUS_OK;
292         }
293         status = pvfs_xattr_ndr_load(pvfs, eas, name->full_name, fd, XATTR_DOSEAS_NAME,
294                                      eas, (ndr_pull_flags_fn_t)ndr_pull_xattr_DosEAs);
295         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
296                 return NT_STATUS_OK;
297         }
298         return status;
299 }
300
301 /*
302   save the set of DOS EAs
303 */
304 NTSTATUS pvfs_doseas_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
305                           struct xattr_DosEAs *eas)
306 {
307         if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
308                 return NT_STATUS_OK;
309         }
310         return pvfs_xattr_ndr_save(pvfs, name->full_name, fd, XATTR_DOSEAS_NAME, eas, 
311                                    (ndr_push_flags_fn_t)ndr_push_xattr_DosEAs);
312 }
313
314
315 /*
316   load the set of streams from extended attributes
317 */
318 NTSTATUS pvfs_streams_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
319                            struct xattr_DosStreams *streams)
320 {
321         NTSTATUS status;
322         ZERO_STRUCTP(streams);
323         if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
324                 return NT_STATUS_OK;
325         }
326         status = pvfs_xattr_ndr_load(pvfs, streams, name->full_name, fd, 
327                                      XATTR_DOSSTREAMS_NAME,
328                                      streams, 
329                                      (ndr_pull_flags_fn_t)ndr_pull_xattr_DosStreams);
330         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
331                 return NT_STATUS_OK;
332         }
333         return status;
334 }
335
336 /*
337   save the set of streams into filesystem xattr
338 */
339 NTSTATUS pvfs_streams_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
340                            struct xattr_DosStreams *streams)
341 {
342         if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
343                 return NT_STATUS_OK;
344         }
345         return pvfs_xattr_ndr_save(pvfs, name->full_name, fd, 
346                                    XATTR_DOSSTREAMS_NAME, 
347                                    streams, 
348                                    (ndr_push_flags_fn_t)ndr_push_xattr_DosStreams);
349 }
350
351
352 /*
353   load the current ACL from extended attributes
354 */
355 NTSTATUS pvfs_acl_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
356                        struct xattr_NTACL *acl)
357 {
358         NTSTATUS status;
359         ZERO_STRUCTP(acl);
360         if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
361                 return NT_STATUS_OK;
362         }
363         status = pvfs_xattr_ndr_load(pvfs, acl, name->full_name, fd, 
364                                      XATTR_NTACL_NAME,
365                                      acl, 
366                                      (ndr_pull_flags_fn_t)ndr_pull_xattr_NTACL);
367         return status;
368 }
369
370 /*
371   save the acl for a file into filesystem xattr
372 */
373 NTSTATUS pvfs_acl_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
374                        struct xattr_NTACL *acl)
375 {
376         NTSTATUS status;
377         void *privs;
378
379         if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
380                 return NT_STATUS_OK;
381         }
382
383         /* this xattr is in the "system" namespace, so we need
384            admin privileges to set it */
385         privs = root_privileges();
386         status = pvfs_xattr_ndr_save(pvfs, name->full_name, fd, 
387                                      XATTR_NTACL_NAME, 
388                                      acl, 
389                                      (ndr_push_flags_fn_t)ndr_push_xattr_NTACL);
390         talloc_free(privs);
391         return status;
392 }
393
394 /*
395   create a zero length xattr with the given name
396 */
397 NTSTATUS pvfs_xattr_create(struct pvfs_state *pvfs, 
398                            const char *fname, int fd,
399                            const char *attr_prefix,
400                            const char *attr_name)
401 {
402         NTSTATUS status;
403         DATA_BLOB blob = data_blob(NULL, 0);
404         char *aname = talloc_asprintf(NULL, "%s%s", attr_prefix, attr_name);
405         if (aname == NULL) {
406                 return NT_STATUS_NO_MEMORY;
407         }
408         status = push_xattr_blob(pvfs, aname, fname, fd, &blob);
409         talloc_free(aname);
410         return status;
411 }
412
413
414 /*
415   delete a xattr with the given name
416 */
417 NTSTATUS pvfs_xattr_delete(struct pvfs_state *pvfs, 
418                            const char *fname, int fd,
419                            const char *attr_prefix,
420                            const char *attr_name)
421 {
422         NTSTATUS status;
423         char *aname = talloc_asprintf(NULL, "%s%s", attr_prefix, attr_name);
424         if (aname == NULL) {
425                 return NT_STATUS_NO_MEMORY;
426         }
427         status = delete_xattr(pvfs, aname, fname, fd);
428         talloc_free(aname);
429         return status;
430 }
431
432 /*
433   load a xattr with the given name
434 */
435 NTSTATUS pvfs_xattr_load(struct pvfs_state *pvfs, 
436                          TALLOC_CTX *mem_ctx,
437                          const char *fname, int fd,
438                          const char *attr_prefix,
439                          const char *attr_name,
440                          size_t estimated_size,
441                          DATA_BLOB *blob)
442 {
443         NTSTATUS status;
444         char *aname = talloc_asprintf(mem_ctx, "%s%s", attr_prefix, attr_name);
445         if (aname == NULL) {
446                 return NT_STATUS_NO_MEMORY;
447         }
448         status = pull_xattr_blob(pvfs, mem_ctx, aname, fname, fd, estimated_size, blob);
449         talloc_free(aname);
450         return status;
451 }
452
453 /*
454   save a xattr with the given name
455 */
456 NTSTATUS pvfs_xattr_save(struct pvfs_state *pvfs, 
457                          const char *fname, int fd,
458                          const char *attr_prefix,
459                          const char *attr_name,
460                          const DATA_BLOB *blob)
461 {
462         NTSTATUS status;
463         char *aname = talloc_asprintf(NULL, "%s%s", attr_prefix, attr_name);
464         if (aname == NULL) {
465                 return NT_STATUS_NO_MEMORY;
466         }
467         status = push_xattr_blob(pvfs, aname, fname, fd, blob);
468         talloc_free(aname);
469         return status;
470 }