s3-xattr_tdb: Use talloc_stackframe() more to allow calling from common code
[samba.git] / source3 / lib / xattr_tdb.c
1 /*
2  * Store posix-level xattrs in a tdb
3  *
4  * Copyright (C) Andrew Bartlett 2011
5  *
6  * extracted from vfs_xattr_tdb by
7  *
8  * Copyright (C) Volker Lendecke, 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 #include "system/filesys.h"
26 #include "librpc/gen_ndr/xattr.h"
27 #include "librpc/gen_ndr/ndr_xattr.h"
28 #include "librpc/gen_ndr/file_id.h"
29 #include "dbwrap/dbwrap.h"
30 #include "lib/util/util_tdb.h"
31 #include "source3/lib/xattr_tdb.h"
32 #include "source3/lib/file_id.h"
33
34 #undef DBGC_CLASS
35 #define DBGC_CLASS DBGC_VFS
36
37 /*
38  * unmarshall tdb_xattrs
39  */
40
41 static NTSTATUS xattr_tdb_pull_attrs(TALLOC_CTX *mem_ctx,
42                                      const TDB_DATA *data,
43                                      struct tdb_xattrs **presult)
44 {
45         DATA_BLOB blob;
46         enum ndr_err_code ndr_err;
47         struct tdb_xattrs *result;
48
49         if (!(result = talloc_zero(mem_ctx, struct tdb_xattrs))) {
50                 return NT_STATUS_NO_MEMORY;
51         }
52
53         if (data->dsize == 0) {
54                 *presult = result;
55                 return NT_STATUS_OK;
56         }
57
58         blob = data_blob_const(data->dptr, data->dsize);
59
60         ndr_err = ndr_pull_struct_blob(&blob, result, result,
61                 (ndr_pull_flags_fn_t)ndr_pull_tdb_xattrs);
62
63         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
64                 DEBUG(0, ("ndr_pull_tdb_xattrs failed: %s\n",
65                           ndr_errstr(ndr_err)));
66                 TALLOC_FREE(result);
67                 return ndr_map_error2ntstatus(ndr_err);
68         }
69
70         *presult = result;
71         return NT_STATUS_OK;
72 }
73
74 /*
75  * marshall tdb_xattrs
76  */
77
78 static NTSTATUS xattr_tdb_push_attrs(TALLOC_CTX *mem_ctx,
79                                      const struct tdb_xattrs *attribs,
80                                      TDB_DATA *data)
81 {
82         DATA_BLOB blob;
83         enum ndr_err_code ndr_err;
84
85         ndr_err = ndr_push_struct_blob(&blob, mem_ctx, attribs,
86                 (ndr_push_flags_fn_t)ndr_push_tdb_xattrs);
87
88         if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
89                 DEBUG(0, ("ndr_push_tdb_xattrs failed: %s\n",
90                           ndr_errstr(ndr_err)));
91                 return ndr_map_error2ntstatus(ndr_err);
92         }
93
94         *data = make_tdb_data(blob.data, blob.length);
95         return NT_STATUS_OK;
96 }
97
98 /*
99  * Load tdb_xattrs for a file from the tdb
100  */
101
102 static NTSTATUS xattr_tdb_load_attrs(TALLOC_CTX *mem_ctx,
103                                      struct db_context *db_ctx,
104                                      const struct file_id *id,
105                                      struct tdb_xattrs **presult)
106 {
107         uint8_t id_buf[16];
108         NTSTATUS status;
109         TDB_DATA data;
110
111         /* For backwards compatibility only store the dev/inode. */
112         push_file_id_16((char *)id_buf, id);
113
114         status = dbwrap_fetch(db_ctx, mem_ctx,
115                               make_tdb_data(id_buf, sizeof(id_buf)),
116                               &data);
117         if (!NT_STATUS_IS_OK(status)) {
118                 return NT_STATUS_INTERNAL_DB_CORRUPTION;
119         }
120
121         status = xattr_tdb_pull_attrs(mem_ctx, &data, presult);
122         TALLOC_FREE(data.dptr);
123         return status;
124 }
125
126 /*
127  * fetch_lock the tdb_ea record for a file
128  */
129
130 static struct db_record *xattr_tdb_lock_attrs(TALLOC_CTX *mem_ctx,
131                                               struct db_context *db_ctx,
132                                               const struct file_id *id)
133 {
134         uint8_t id_buf[16];
135
136         /* For backwards compatibility only store the dev/inode. */
137         push_file_id_16((char *)id_buf, id);
138         return dbwrap_fetch_locked(db_ctx, mem_ctx,
139                                    make_tdb_data(id_buf, sizeof(id_buf)));
140 }
141
142 /*
143  * Save tdb_xattrs to a previously fetch_locked record
144  */
145
146 static NTSTATUS xattr_tdb_save_attrs(struct db_record *rec,
147                                      const struct tdb_xattrs *attribs)
148 {
149         TDB_DATA data = tdb_null;
150         NTSTATUS status;
151
152         status = xattr_tdb_push_attrs(talloc_tos(), attribs, &data);
153
154         if (!NT_STATUS_IS_OK(status)) {
155                 DEBUG(0, ("xattr_tdb_push_attrs failed: %s\n",
156                           nt_errstr(status)));
157                 return status;
158         }
159
160         status = dbwrap_record_store(rec, data, 0);
161
162         TALLOC_FREE(data.dptr);
163
164         return status;
165 }
166
167 /*
168  * Worker routine for getxattr and fgetxattr
169  */
170
171 ssize_t xattr_tdb_getattr(struct db_context *db_ctx,
172                           TALLOC_CTX *mem_ctx,
173                           const struct file_id *id,
174                           const char *name, DATA_BLOB *blob)
175 {
176         struct tdb_xattrs *attribs;
177         uint32_t i;
178         ssize_t result = -1;
179         NTSTATUS status;
180         TALLOC_CTX *frame = talloc_stackframe();
181         if (!frame) {
182                 errno = ENOMEM;
183                 return -1;
184         }
185
186         DEBUG(10, ("xattr_tdb_getattr called for file %s, name %s\n",
187                    file_id_string(frame, id), name));
188
189         status = xattr_tdb_load_attrs(frame, db_ctx, id, &attribs);
190
191         if (!NT_STATUS_IS_OK(status)) {
192                 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
193                            nt_errstr(status)));
194                 TALLOC_FREE(frame);
195                 errno = EINVAL;
196                 return -1;
197         }
198
199         for (i=0; i<attribs->num_eas; i++) {
200                 if (strcmp(attribs->eas[i].name, name) == 0) {
201                         break;
202                 }
203         }
204
205         if (i == attribs->num_eas) {
206                 errno = ENOATTR;
207                 goto fail;
208         }
209
210         *blob = attribs->eas[i].value;
211         talloc_steal(mem_ctx, blob->data);
212         result = attribs->eas[i].value.length;
213
214  fail:
215         TALLOC_FREE(frame);
216         return result;
217 }
218
219 /*
220  * Worker routine for setxattr and fsetxattr
221  */
222
223 int xattr_tdb_setattr(struct db_context *db_ctx,
224                       const struct file_id *id, const char *name,
225                       const void *value, size_t size, int flags)
226 {
227         NTSTATUS status;
228         struct db_record *rec;
229         struct tdb_xattrs *attribs;
230         uint32_t i;
231         TDB_DATA data;
232         TALLOC_CTX *frame = talloc_stackframe();
233         if (!frame) {
234                 errno = ENOMEM;
235                 return -1;
236         }
237
238         DEBUG(10, ("xattr_tdb_setattr called for file %s, name %s\n",
239                    file_id_string(frame, id), name));
240
241         rec = xattr_tdb_lock_attrs(frame, db_ctx, id);
242
243         if (rec == NULL) {
244                 DEBUG(0, ("xattr_tdb_lock_attrs failed\n"));
245                 errno = EINVAL;
246                 return -1;
247         }
248
249         data = dbwrap_record_get_value(rec);
250
251         status = xattr_tdb_pull_attrs(rec, &data, &attribs);
252
253         if (!NT_STATUS_IS_OK(status)) {
254                 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
255                            nt_errstr(status)));
256                 TALLOC_FREE(frame);
257                 return -1;
258         }
259
260         for (i=0; i<attribs->num_eas; i++) {
261                 if (strcmp(attribs->eas[i].name, name) == 0) {
262                         if (flags & XATTR_CREATE) {
263                                 TALLOC_FREE(frame);
264                                 errno = EEXIST;
265                                 return -1;
266                         }
267                         break;
268                 }
269         }
270
271         if (i == attribs->num_eas) {
272                 struct xattr_EA *tmp;
273
274                 if (flags & XATTR_REPLACE) {
275                         TALLOC_FREE(frame);
276                         errno = ENOATTR;
277                         return -1;
278                 }
279
280                 tmp = talloc_realloc(
281                         attribs, attribs->eas, struct xattr_EA,
282                         attribs->num_eas+ 1);
283
284                 if (tmp == NULL) {
285                         DEBUG(0, ("talloc_realloc failed\n"));
286                         TALLOC_FREE(frame);
287                         errno = ENOMEM;
288                         return -1;
289                 }
290
291                 attribs->eas = tmp;
292                 attribs->num_eas += 1;
293         }
294
295         attribs->eas[i].name = name;
296         attribs->eas[i].value.data = discard_const_p(uint8_t, value);
297         attribs->eas[i].value.length = size;
298
299         status = xattr_tdb_save_attrs(rec, attribs);
300
301         TALLOC_FREE(frame);
302
303         if (!NT_STATUS_IS_OK(status)) {
304                 DEBUG(1, ("save failed: %s\n", nt_errstr(status)));
305                 return -1;
306         }
307
308         return 0;
309 }
310
311 /*
312  * Worker routine for listxattr and flistxattr
313  */
314
315 ssize_t xattr_tdb_listattr(struct db_context *db_ctx,
316                            const struct file_id *id, char *list,
317                            size_t size)
318 {
319         NTSTATUS status;
320         struct tdb_xattrs *attribs;
321         uint32_t i;
322         size_t len = 0;
323         TALLOC_CTX *frame = talloc_stackframe();
324         if (!frame) {
325                 errno = ENOMEM;
326                 return -1;
327         }
328
329         status = xattr_tdb_load_attrs(frame, db_ctx, id, &attribs);
330
331         if (!NT_STATUS_IS_OK(status)) {
332                 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
333                            nt_errstr(status)));
334                 errno = EINVAL;
335                 TALLOC_FREE(frame);
336                 return -1;
337         }
338
339         DEBUG(10, ("xattr_tdb_listattr: Found %d xattrs\n",
340                    attribs->num_eas));
341
342         for (i=0; i<attribs->num_eas; i++) {
343                 size_t tmp;
344
345                 DEBUG(10, ("xattr_tdb_listattr: xattrs[i].name: %s\n",
346                            attribs->eas[i].name));
347
348                 tmp = strlen(attribs->eas[i].name);
349
350                 /*
351                  * Try to protect against overflow
352                  */
353
354                 if (len + (tmp+1) < len) {
355                         TALLOC_FREE(frame);
356                         errno = EINVAL;
357                         return -1;
358                 }
359
360                 /*
361                  * Take care of the terminating NULL
362                  */
363                 len += (tmp + 1);
364         }
365
366         if (len > size) {
367                 TALLOC_FREE(frame);
368                 errno = ERANGE;
369                 return len;
370         }
371
372         len = 0;
373
374         for (i=0; i<attribs->num_eas; i++) {
375                 strlcpy(list+len, attribs->eas[i].name,
376                         size-len);
377                 len += (strlen(attribs->eas[i].name) + 1);
378         }
379
380         TALLOC_FREE(frame);
381         return len;
382 }
383
384 /*
385  * Worker routine for removexattr and fremovexattr
386  */
387
388 int xattr_tdb_removeattr(struct db_context *db_ctx,
389                          const struct file_id *id, const char *name)
390 {
391         NTSTATUS status;
392         struct db_record *rec;
393         struct tdb_xattrs *attribs;
394         uint32_t i;
395         TDB_DATA value;
396
397         rec = xattr_tdb_lock_attrs(talloc_tos(), db_ctx, id);
398
399         if (rec == NULL) {
400                 DEBUG(0, ("xattr_tdb_lock_attrs failed\n"));
401                 errno = EINVAL;
402                 return -1;
403         }
404
405         value = dbwrap_record_get_value(rec);
406
407         status = xattr_tdb_pull_attrs(rec, &value, &attribs);
408
409         if (!NT_STATUS_IS_OK(status)) {
410                 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
411                            nt_errstr(status)));
412                 TALLOC_FREE(rec);
413                 return -1;
414         }
415
416         for (i=0; i<attribs->num_eas; i++) {
417                 if (strcmp(attribs->eas[i].name, name) == 0) {
418                         break;
419                 }
420         }
421
422         if (i == attribs->num_eas) {
423                 TALLOC_FREE(rec);
424                 errno = ENOATTR;
425                 return -1;
426         }
427
428         attribs->eas[i] =
429                 attribs->eas[attribs->num_eas-1];
430         attribs->num_eas -= 1;
431
432         if (attribs->num_eas == 0) {
433                 dbwrap_record_delete(rec);
434                 TALLOC_FREE(rec);
435                 return 0;
436         }
437
438         status = xattr_tdb_save_attrs(rec, attribs);
439
440         TALLOC_FREE(rec);
441
442         if (!NT_STATUS_IS_OK(status)) {
443                 DEBUG(1, ("save failed: %s\n", nt_errstr(status)));
444                 return -1;
445         }
446
447         return 0;
448 }
449
450 /*
451  * Worker routine for unlink and rmdir
452  */
453
454 void xattr_tdb_remove_all_attrs(struct db_context *db_ctx,
455                                const struct file_id *id)
456 {
457         struct db_record *rec;
458         rec = xattr_tdb_lock_attrs(talloc_tos(), db_ctx, id);
459
460         /*
461          * If rec == NULL there's not much we can do about it
462          */
463
464         if (rec != NULL) {
465                 dbwrap_record_delete(rec);
466                 TALLOC_FREE(rec);
467         }
468 }