source: trunk/server/source3/modules/vfs_xattr_tdb.c

Last change on this file was 745, checked in by Silvan Scherrer, 13 years ago

Samba Server: updated trunk to 3.6.0

File size: 17.1 KB
Line 
1/*
2 * Store posix-level xattrs in a tdb
3 *
4 * Copyright (C) Volker Lendecke, 2007
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "includes.h"
21#include "system/filesys.h"
22#include "smbd/smbd.h"
23#include "librpc/gen_ndr/xattr.h"
24#include "librpc/gen_ndr/ndr_xattr.h"
25#include "../librpc/gen_ndr/ndr_netlogon.h"
26#include "dbwrap.h"
27#include "util_tdb.h"
28
29#undef DBGC_CLASS
30#define DBGC_CLASS DBGC_VFS
31
32/*
33 * unmarshall tdb_xattrs
34 */
35
36static NTSTATUS xattr_tdb_pull_attrs(TALLOC_CTX *mem_ctx,
37 const TDB_DATA *data,
38 struct tdb_xattrs **presult)
39{
40 DATA_BLOB blob;
41 enum ndr_err_code ndr_err;
42 struct tdb_xattrs *result;
43
44 if (!(result = TALLOC_ZERO_P(mem_ctx, struct tdb_xattrs))) {
45 return NT_STATUS_NO_MEMORY;
46 }
47
48 if (data->dsize == 0) {
49 *presult = result;
50 return NT_STATUS_OK;
51 }
52
53 blob = data_blob_const(data->dptr, data->dsize);
54
55 ndr_err = ndr_pull_struct_blob(&blob, result, result,
56 (ndr_pull_flags_fn_t)ndr_pull_tdb_xattrs);
57
58 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
59 DEBUG(0, ("ndr_pull_tdb_xattrs failed: %s\n",
60 ndr_errstr(ndr_err)));
61 TALLOC_FREE(result);
62 return ndr_map_error2ntstatus(ndr_err);
63 }
64
65 *presult = result;
66 return NT_STATUS_OK;
67}
68
69/*
70 * marshall tdb_xattrs
71 */
72
73static NTSTATUS xattr_tdb_push_attrs(TALLOC_CTX *mem_ctx,
74 const struct tdb_xattrs *attribs,
75 TDB_DATA *data)
76{
77 DATA_BLOB blob;
78 enum ndr_err_code ndr_err;
79
80 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, attribs,
81 (ndr_push_flags_fn_t)ndr_push_tdb_xattrs);
82
83 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
84 DEBUG(0, ("ndr_push_tdb_xattrs failed: %s\n",
85 ndr_errstr(ndr_err)));
86 return ndr_map_error2ntstatus(ndr_err);
87 }
88
89 *data = make_tdb_data(blob.data, blob.length);
90 return NT_STATUS_OK;
91}
92
93/*
94 * Load tdb_xattrs for a file from the tdb
95 */
96
97static NTSTATUS xattr_tdb_load_attrs(TALLOC_CTX *mem_ctx,
98 struct db_context *db_ctx,
99 const struct file_id *id,
100 struct tdb_xattrs **presult)
101{
102 uint8 id_buf[16];
103 NTSTATUS status;
104 TDB_DATA data;
105
106 /* For backwards compatibility only store the dev/inode. */
107 push_file_id_16((char *)id_buf, id);
108
109 if (db_ctx->fetch(db_ctx, mem_ctx,
110 make_tdb_data(id_buf, sizeof(id_buf)),
111 &data) == -1) {
112 return NT_STATUS_INTERNAL_DB_CORRUPTION;
113 }
114
115 status = xattr_tdb_pull_attrs(mem_ctx, &data, presult);
116 TALLOC_FREE(data.dptr);
117 return status;
118}
119
120/*
121 * fetch_lock the tdb_ea record for a file
122 */
123
124static struct db_record *xattr_tdb_lock_attrs(TALLOC_CTX *mem_ctx,
125 struct db_context *db_ctx,
126 const struct file_id *id)
127{
128 uint8 id_buf[16];
129
130 /* For backwards compatibility only store the dev/inode. */
131 push_file_id_16((char *)id_buf, id);
132 return db_ctx->fetch_locked(db_ctx, mem_ctx,
133 make_tdb_data(id_buf, sizeof(id_buf)));
134}
135
136/*
137 * Save tdb_xattrs to a previously fetch_locked record
138 */
139
140static NTSTATUS xattr_tdb_save_attrs(struct db_record *rec,
141 const struct tdb_xattrs *attribs)
142{
143 TDB_DATA data = tdb_null;
144 NTSTATUS status;
145
146 status = xattr_tdb_push_attrs(talloc_tos(), attribs, &data);
147
148 if (!NT_STATUS_IS_OK(status)) {
149 DEBUG(0, ("xattr_tdb_push_attrs failed: %s\n",
150 nt_errstr(status)));
151 return status;
152 }
153
154 status = rec->store(rec, data, 0);
155
156 TALLOC_FREE(data.dptr);
157
158 return status;
159}
160
161/*
162 * Worker routine for getxattr and fgetxattr
163 */
164
165static ssize_t xattr_tdb_getattr(struct db_context *db_ctx,
166 const struct file_id *id,
167 const char *name, void *value, size_t size)
168{
169 struct tdb_xattrs *attribs;
170 uint32_t i;
171 ssize_t result = -1;
172 NTSTATUS status;
173
174 DEBUG(10, ("xattr_tdb_getattr called for file %s, name %s\n",
175 file_id_string_tos(id), name));
176
177 status = xattr_tdb_load_attrs(talloc_tos(), db_ctx, id, &attribs);
178
179 if (!NT_STATUS_IS_OK(status)) {
180 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
181 nt_errstr(status)));
182 errno = EINVAL;
183 return -1;
184 }
185
186 for (i=0; i<attribs->num_eas; i++) {
187 if (strcmp(attribs->eas[i].name, name) == 0) {
188 break;
189 }
190 }
191
192 if (i == attribs->num_eas) {
193 errno = ENOATTR;
194 goto fail;
195 }
196
197 if (attribs->eas[i].value.length > size) {
198 errno = ERANGE;
199 goto fail;
200 }
201
202 memcpy(value, attribs->eas[i].value.data,
203 attribs->eas[i].value.length);
204 result = attribs->eas[i].value.length;
205
206 fail:
207 TALLOC_FREE(attribs);
208 return result;
209}
210
211static ssize_t xattr_tdb_getxattr(struct vfs_handle_struct *handle,
212 const char *path, const char *name,
213 void *value, size_t size)
214{
215 SMB_STRUCT_STAT sbuf;
216 struct file_id id;
217 struct db_context *db;
218
219 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
220
221 if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
222 return -1;
223 }
224
225 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
226
227 return xattr_tdb_getattr(db, &id, name, value, size);
228}
229
230static ssize_t xattr_tdb_fgetxattr(struct vfs_handle_struct *handle,
231 struct files_struct *fsp,
232 const char *name, void *value, size_t size)
233{
234 SMB_STRUCT_STAT sbuf;
235 struct file_id id;
236 struct db_context *db;
237
238 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
239
240 if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
241 return -1;
242 }
243
244 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
245
246 return xattr_tdb_getattr(db, &id, name, value, size);
247}
248
249/*
250 * Worker routine for setxattr and fsetxattr
251 */
252
253static int xattr_tdb_setattr(struct db_context *db_ctx,
254 const struct file_id *id, const char *name,
255 const void *value, size_t size, int flags)
256{
257 NTSTATUS status;
258 struct db_record *rec;
259 struct tdb_xattrs *attribs;
260 uint32_t i;
261
262 DEBUG(10, ("xattr_tdb_setattr called for file %s, name %s\n",
263 file_id_string_tos(id), name));
264
265 rec = xattr_tdb_lock_attrs(talloc_tos(), db_ctx, id);
266
267 if (rec == NULL) {
268 DEBUG(0, ("xattr_tdb_lock_attrs failed\n"));
269 errno = EINVAL;
270 return -1;
271 }
272
273 status = xattr_tdb_pull_attrs(rec, &rec->value, &attribs);
274
275 if (!NT_STATUS_IS_OK(status)) {
276 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
277 nt_errstr(status)));
278 TALLOC_FREE(rec);
279 return -1;
280 }
281
282 for (i=0; i<attribs->num_eas; i++) {
283 if (strcmp(attribs->eas[i].name, name) == 0) {
284 if (flags & XATTR_CREATE) {
285 TALLOC_FREE(rec);
286 errno = EEXIST;
287 return -1;
288 }
289 break;
290 }
291 }
292
293 if (i == attribs->num_eas) {
294 struct xattr_EA *tmp;
295
296 if (flags & XATTR_REPLACE) {
297 TALLOC_FREE(rec);
298 errno = ENOATTR;
299 return -1;
300 }
301
302 tmp = TALLOC_REALLOC_ARRAY(
303 attribs, attribs->eas, struct xattr_EA,
304 attribs->num_eas+ 1);
305
306 if (tmp == NULL) {
307 DEBUG(0, ("TALLOC_REALLOC_ARRAY failed\n"));
308 TALLOC_FREE(rec);
309 errno = ENOMEM;
310 return -1;
311 }
312
313 attribs->eas = tmp;
314 attribs->num_eas += 1;
315 }
316
317 attribs->eas[i].name = name;
318 attribs->eas[i].value.data = CONST_DISCARD(uint8 *, value);
319 attribs->eas[i].value.length = size;
320
321 status = xattr_tdb_save_attrs(rec, attribs);
322
323 TALLOC_FREE(rec);
324
325 if (!NT_STATUS_IS_OK(status)) {
326 DEBUG(1, ("save failed: %s\n", nt_errstr(status)));
327 return -1;
328 }
329
330 return 0;
331}
332
333static int xattr_tdb_setxattr(struct vfs_handle_struct *handle,
334 const char *path, const char *name,
335 const void *value, size_t size, int flags)
336{
337 SMB_STRUCT_STAT sbuf;
338 struct file_id id;
339 struct db_context *db;
340
341 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
342
343 if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
344 return -1;
345 }
346
347 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
348
349 return xattr_tdb_setattr(db, &id, name, value, size, flags);
350}
351
352static int xattr_tdb_fsetxattr(struct vfs_handle_struct *handle,
353 struct files_struct *fsp,
354 const char *name, const void *value,
355 size_t size, int flags)
356{
357 SMB_STRUCT_STAT sbuf;
358 struct file_id id;
359 struct db_context *db;
360
361 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
362
363 if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
364 return -1;
365 }
366
367 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
368
369 return xattr_tdb_setattr(db, &id, name, value, size, flags);
370}
371
372/*
373 * Worker routine for listxattr and flistxattr
374 */
375
376static ssize_t xattr_tdb_listattr(struct db_context *db_ctx,
377 const struct file_id *id, char *list,
378 size_t size)
379{
380 NTSTATUS status;
381 struct tdb_xattrs *attribs;
382 uint32_t i;
383 size_t len = 0;
384
385 status = xattr_tdb_load_attrs(talloc_tos(), db_ctx, id, &attribs);
386
387 if (!NT_STATUS_IS_OK(status)) {
388 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
389 nt_errstr(status)));
390 errno = EINVAL;
391 return -1;
392 }
393
394 DEBUG(10, ("xattr_tdb_listattr: Found %d xattrs\n",
395 attribs->num_eas));
396
397 for (i=0; i<attribs->num_eas; i++) {
398 size_t tmp;
399
400 DEBUG(10, ("xattr_tdb_listattr: xattrs[i].name: %s\n",
401 attribs->eas[i].name));
402
403 tmp = strlen(attribs->eas[i].name);
404
405 /*
406 * Try to protect against overflow
407 */
408
409 if (len + (tmp+1) < len) {
410 TALLOC_FREE(attribs);
411 errno = EINVAL;
412 return -1;
413 }
414
415 /*
416 * Take care of the terminating NULL
417 */
418 len += (tmp + 1);
419 }
420
421 if (len > size) {
422 TALLOC_FREE(attribs);
423 errno = ERANGE;
424 return -1;
425 }
426
427 len = 0;
428
429 for (i=0; i<attribs->num_eas; i++) {
430 strlcpy(list+len, attribs->eas[i].name,
431 size-len);
432 len += (strlen(attribs->eas[i].name) + 1);
433 }
434
435 TALLOC_FREE(attribs);
436 return len;
437}
438
439static ssize_t xattr_tdb_listxattr(struct vfs_handle_struct *handle,
440 const char *path, char *list, size_t size)
441{
442 SMB_STRUCT_STAT sbuf;
443 struct file_id id;
444 struct db_context *db;
445
446 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
447
448 if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
449 return -1;
450 }
451
452 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
453
454 return xattr_tdb_listattr(db, &id, list, size);
455}
456
457static ssize_t xattr_tdb_flistxattr(struct vfs_handle_struct *handle,
458 struct files_struct *fsp, char *list,
459 size_t size)
460{
461 SMB_STRUCT_STAT sbuf;
462 struct file_id id;
463 struct db_context *db;
464
465 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
466
467 if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
468 return -1;
469 }
470
471 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
472
473 return xattr_tdb_listattr(db, &id, list, size);
474}
475
476/*
477 * Worker routine for removexattr and fremovexattr
478 */
479
480static int xattr_tdb_removeattr(struct db_context *db_ctx,
481 const struct file_id *id, const char *name)
482{
483 NTSTATUS status;
484 struct db_record *rec;
485 struct tdb_xattrs *attribs;
486 uint32_t i;
487
488 rec = xattr_tdb_lock_attrs(talloc_tos(), db_ctx, id);
489
490 if (rec == NULL) {
491 DEBUG(0, ("xattr_tdb_lock_attrs failed\n"));
492 errno = EINVAL;
493 return -1;
494 }
495
496 status = xattr_tdb_pull_attrs(rec, &rec->value, &attribs);
497
498 if (!NT_STATUS_IS_OK(status)) {
499 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
500 nt_errstr(status)));
501 TALLOC_FREE(rec);
502 return -1;
503 }
504
505 for (i=0; i<attribs->num_eas; i++) {
506 if (strcmp(attribs->eas[i].name, name) == 0) {
507 break;
508 }
509 }
510
511 if (i == attribs->num_eas) {
512 TALLOC_FREE(rec);
513 errno = ENOATTR;
514 return -1;
515 }
516
517 attribs->eas[i] =
518 attribs->eas[attribs->num_eas-1];
519 attribs->num_eas -= 1;
520
521 if (attribs->num_eas == 0) {
522 rec->delete_rec(rec);
523 TALLOC_FREE(rec);
524 return 0;
525 }
526
527 status = xattr_tdb_save_attrs(rec, attribs);
528
529 TALLOC_FREE(rec);
530
531 if (!NT_STATUS_IS_OK(status)) {
532 DEBUG(1, ("save failed: %s\n", nt_errstr(status)));
533 return -1;
534 }
535
536 return 0;
537}
538
539static int xattr_tdb_removexattr(struct vfs_handle_struct *handle,
540 const char *path, const char *name)
541{
542 SMB_STRUCT_STAT sbuf;
543 struct file_id id;
544 struct db_context *db;
545
546 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
547
548 if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
549 return -1;
550 }
551
552 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
553
554 return xattr_tdb_removeattr(db, &id, name);
555}
556
557static int xattr_tdb_fremovexattr(struct vfs_handle_struct *handle,
558 struct files_struct *fsp, const char *name)
559{
560 SMB_STRUCT_STAT sbuf;
561 struct file_id id;
562 struct db_context *db;
563
564 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
565
566 if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
567 return -1;
568 }
569
570 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
571
572 return xattr_tdb_removeattr(db, &id, name);
573}
574
575/*
576 * Open the tdb file upon VFS_CONNECT
577 */
578
579static bool xattr_tdb_init(int snum, struct db_context **p_db)
580{
581 struct db_context *db;
582 const char *dbname;
583 char *def_dbname;
584
585 def_dbname = state_path("xattr.tdb");
586 if (def_dbname == NULL) {
587 errno = ENOSYS;
588 return false;
589 }
590
591 dbname = lp_parm_const_string(snum, "xattr_tdb", "file", def_dbname);
592
593 /* now we know dbname is not NULL */
594
595 become_root();
596 db = db_open(NULL, dbname, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
597 unbecome_root();
598
599 if (db == NULL) {
600#if defined(ENOTSUP)
601 errno = ENOTSUP;
602#else
603 errno = ENOSYS;
604#endif
605 TALLOC_FREE(def_dbname);
606 return false;
607 }
608
609 *p_db = db;
610 TALLOC_FREE(def_dbname);
611 return true;
612}
613
614/*
615 * On unlink we need to delete the tdb record
616 */
617static int xattr_tdb_unlink(vfs_handle_struct *handle,
618 const struct smb_filename *smb_fname)
619{
620 struct smb_filename *smb_fname_tmp = NULL;
621 struct file_id id;
622 struct db_context *db;
623 struct db_record *rec;
624 NTSTATUS status;
625 int ret = -1;
626 bool remove_record = false;
627
628 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
629
630 status = copy_smb_filename(talloc_tos(), smb_fname, &smb_fname_tmp);
631 if (!NT_STATUS_IS_OK(status)) {
632 errno = map_errno_from_nt_status(status);
633 return -1;
634 }
635
636 if (lp_posix_pathnames()) {
637 ret = SMB_VFS_LSTAT(handle->conn, smb_fname_tmp);
638 } else {
639 ret = SMB_VFS_STAT(handle->conn, smb_fname_tmp);
640 }
641 if (ret == -1) {
642 goto out;
643 }
644
645 if (smb_fname_tmp->st.st_ex_nlink == 1) {
646 /* Only remove record on last link to file. */
647 remove_record = true;
648 }
649
650 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname_tmp);
651
652 if (ret == -1) {
653 goto out;
654 }
655
656 if (!remove_record) {
657 goto out;
658 }
659
660 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &smb_fname_tmp->st);
661
662 rec = xattr_tdb_lock_attrs(talloc_tos(), db, &id);
663
664 /*
665 * If rec == NULL there's not much we can do about it
666 */
667
668 if (rec != NULL) {
669 rec->delete_rec(rec);
670 TALLOC_FREE(rec);
671 }
672
673 out:
674 TALLOC_FREE(smb_fname_tmp);
675 return ret;
676}
677
678/*
679 * On rmdir we need to delete the tdb record
680 */
681static int xattr_tdb_rmdir(vfs_handle_struct *handle, const char *path)
682{
683 SMB_STRUCT_STAT sbuf;
684 struct file_id id;
685 struct db_context *db;
686 struct db_record *rec;
687 int ret;
688
689 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
690
691 if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
692 return -1;
693 }
694
695 ret = SMB_VFS_NEXT_RMDIR(handle, path);
696
697 if (ret == -1) {
698 return -1;
699 }
700
701 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
702
703 rec = xattr_tdb_lock_attrs(talloc_tos(), db, &id);
704
705 /*
706 * If rec == NULL there's not much we can do about it
707 */
708
709 if (rec != NULL) {
710 rec->delete_rec(rec);
711 TALLOC_FREE(rec);
712 }
713
714 return 0;
715}
716
717/*
718 * Destructor for the VFS private data
719 */
720
721static void close_xattr_db(void **data)
722{
723 struct db_context **p_db = (struct db_context **)data;
724 TALLOC_FREE(*p_db);
725}
726
727static int xattr_tdb_connect(vfs_handle_struct *handle, const char *service,
728 const char *user)
729{
730 char *sname = NULL;
731 int res, snum;
732 struct db_context *db;
733
734 res = SMB_VFS_NEXT_CONNECT(handle, service, user);
735 if (res < 0) {
736 return res;
737 }
738
739 snum = find_service(talloc_tos(), service, &sname);
740 if (snum == -1 || sname == NULL) {
741 /*
742 * Should not happen, but we should not fail just *here*.
743 */
744 return 0;
745 }
746
747 if (!xattr_tdb_init(snum, &db)) {
748 DEBUG(5, ("Could not init xattr tdb\n"));
749 lp_do_parameter(snum, "ea support", "False");
750 return 0;
751 }
752
753 lp_do_parameter(snum, "ea support", "True");
754
755 SMB_VFS_HANDLE_SET_DATA(handle, db, close_xattr_db,
756 struct db_context, return -1);
757
758 return 0;
759}
760
761static struct vfs_fn_pointers vfs_xattr_tdb_fns = {
762 .getxattr = xattr_tdb_getxattr,
763 .fgetxattr = xattr_tdb_fgetxattr,
764 .setxattr = xattr_tdb_setxattr,
765 .fsetxattr = xattr_tdb_fsetxattr,
766 .listxattr = xattr_tdb_listxattr,
767 .flistxattr = xattr_tdb_flistxattr,
768 .removexattr = xattr_tdb_removexattr,
769 .fremovexattr = xattr_tdb_fremovexattr,
770 .unlink = xattr_tdb_unlink,
771 .rmdir = xattr_tdb_rmdir,
772 .connect_fn = xattr_tdb_connect,
773};
774
775NTSTATUS vfs_xattr_tdb_init(void);
776NTSTATUS vfs_xattr_tdb_init(void)
777{
778 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "xattr_tdb",
779 &vfs_xattr_tdb_fns);
780}
Note: See TracBrowser for help on using the repository browser.