r3717: - expanded the RAW-RENAME test a little
[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   load a NDR structure from a xattr
105 */
106 static NTSTATUS pvfs_xattr_ndr_load(struct pvfs_state *pvfs,
107                                     TALLOC_CTX *mem_ctx,
108                                     const char *fname, int fd, const char *attr_name,
109                                     void *p, ndr_pull_flags_fn_t pull_fn)
110 {
111         NTSTATUS status;
112         DATA_BLOB blob;
113
114         status = pull_xattr_blob(pvfs, mem_ctx, attr_name, fname, 
115                                  fd, XATTR_DOSATTRIB_ESTIMATED_SIZE, &blob);
116         if (!NT_STATUS_IS_OK(status)) {
117                 return status;
118         }
119
120         /* pull the blob */
121         status = ndr_pull_struct_blob(&blob, mem_ctx, p, pull_fn);
122
123         data_blob_free(&blob);
124
125         return status;
126 }
127
128 /*
129   save a NDR structure into a xattr
130 */
131 static NTSTATUS pvfs_xattr_ndr_save(struct pvfs_state *pvfs,
132                                     const char *fname, int fd, const char *attr_name, 
133                                     void *p, ndr_push_flags_fn_t push_fn)
134 {
135         TALLOC_CTX *mem_ctx = talloc(NULL, 0);
136         DATA_BLOB blob;
137         NTSTATUS status;
138
139         status = ndr_push_struct_blob(&blob, mem_ctx, p, (ndr_push_flags_fn_t)push_fn);
140         if (!NT_STATUS_IS_OK(status)) {
141                 talloc_free(mem_ctx);
142                 return status;
143         }
144
145         status = push_xattr_blob(pvfs, attr_name, fname, fd, &blob);
146         talloc_free(mem_ctx);
147
148         return status;
149 }
150
151
152 /*
153   fill in file attributes from extended attributes
154 */
155 NTSTATUS pvfs_dosattrib_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd)
156 {
157         NTSTATUS status;
158         struct xattr_DosAttrib attrib;
159         TALLOC_CTX *mem_ctx = talloc(name, 0);
160         struct xattr_DosInfo1 *info1;
161
162         if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
163                 return NT_STATUS_OK;
164         }
165
166         status = pvfs_xattr_ndr_load(pvfs, mem_ctx, name->full_name, 
167                                      fd, XATTR_DOSATTRIB_NAME,
168                                      &attrib, 
169                                      (ndr_pull_flags_fn_t)ndr_pull_xattr_DosAttrib);
170
171         /* if the filesystem doesn't support them, then tell pvfs not to try again */
172         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
173                 DEBUG(5,("pvfs_xattr: xattr not supported in filesystem\n"));
174                 pvfs->flags &= ~PVFS_FLAG_XATTR_ENABLE;
175                 talloc_free(mem_ctx);
176                 return NT_STATUS_OK;
177         }
178
179         /* not having a DosAttrib is not an error */
180         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
181                 talloc_free(mem_ctx);
182                 return NT_STATUS_OK;
183         }
184
185         if (!NT_STATUS_IS_OK(status)) {
186                 talloc_free(mem_ctx);
187                 return status;
188         }
189
190         switch (attrib.version) {
191         case 1:
192                 info1 = &attrib.info.info1;
193                 name->dos.attrib = pvfs_attrib_normalise(info1->attrib);
194                 name->dos.ea_size = info1->ea_size;
195                 if (name->st.st_size == info1->size) {
196                         name->dos.alloc_size = info1->alloc_size;
197                 }
198                 if (info1->create_time != 0) {
199                         name->dos.create_time = info1->create_time;
200                 }
201                 if (info1->change_time != 0) {
202                         name->dos.change_time = info1->change_time;
203                 }
204                 break;
205
206         default:
207                 DEBUG(0,("ERROR: Unsupported xattr DosAttrib version %d on '%s'\n",
208                          attrib.version, name->full_name));
209                 talloc_free(mem_ctx);
210                 return NT_STATUS_INVALID_LEVEL;
211         }
212
213         talloc_free(mem_ctx);
214         return NT_STATUS_OK;
215 }
216
217
218 /*
219   save the file attribute into the xattr
220 */
221 NTSTATUS pvfs_dosattrib_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd)
222 {
223         struct xattr_DosAttrib attrib;
224         struct xattr_DosInfo1 *info1;
225
226         if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
227                 return NT_STATUS_OK;
228         }
229
230         attrib.version = 1;
231         info1 = &attrib.info.info1;
232
233         name->dos.attrib = pvfs_attrib_normalise(name->dos.attrib);
234
235         info1->attrib      = name->dos.attrib;
236         info1->ea_size     = name->dos.ea_size;
237         info1->size        = name->st.st_size;
238         info1->alloc_size  = name->dos.alloc_size;
239         info1->create_time = name->dos.create_time;
240         info1->change_time = name->dos.change_time;
241
242         return pvfs_xattr_ndr_save(pvfs, name->full_name, fd, 
243                                    XATTR_DOSATTRIB_NAME, &attrib, 
244                                    (ndr_push_flags_fn_t)ndr_push_xattr_DosAttrib);
245 }
246
247
248 /*
249   load the set of DOS EAs
250 */
251 NTSTATUS pvfs_doseas_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
252                           struct xattr_DosEAs *eas)
253 {
254         NTSTATUS status;
255         ZERO_STRUCTP(eas);
256         if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
257                 return NT_STATUS_OK;
258         }
259         status = pvfs_xattr_ndr_load(pvfs, eas, name->full_name, fd, XATTR_DOSEAS_NAME,
260                                      eas, (ndr_pull_flags_fn_t)ndr_pull_xattr_DosEAs);
261         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
262                 return NT_STATUS_OK;
263         }
264         return status;
265 }
266
267 /*
268   save the set of DOS EAs
269 */
270 NTSTATUS pvfs_doseas_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
271                           struct xattr_DosEAs *eas)
272 {
273         if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
274                 return NT_STATUS_OK;
275         }
276         return pvfs_xattr_ndr_save(pvfs, name->full_name, fd, XATTR_DOSEAS_NAME, eas, 
277                                    (ndr_push_flags_fn_t)ndr_push_xattr_DosEAs);
278 }