2 * Store posix-level xattrs in a tdb
4 * Copyright (C) Volker Lendecke, 2007
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.
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.
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/>.
21 #include "librpc/gen_ndr/xattr.h"
22 #include "librpc/gen_ndr/ndr_xattr.h"
25 #define DBGC_CLASS DBGC_VFS
28 * unmarshall tdb_xattrs
31 static NTSTATUS xattr_tdb_pull_attrs(TALLOC_CTX *mem_ctx,
33 struct tdb_xattrs **presult)
36 enum ndr_err_code ndr_err;
37 struct tdb_xattrs *result;
39 if (!(result = TALLOC_ZERO_P(mem_ctx, struct tdb_xattrs))) {
40 return NT_STATUS_NO_MEMORY;
43 if (data->dsize == 0) {
48 blob = data_blob_const(data->dptr, data->dsize);
50 ndr_err = ndr_pull_struct_blob(
51 &blob, result, result,
52 (ndr_pull_flags_fn_t)ndr_pull_tdb_xattrs);
54 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
55 DEBUG(0, ("ndr_pull_tdb_xattrs failed: %s\n",
56 ndr_errstr(ndr_err)));
58 return ndr_map_error2ntstatus(ndr_err);;
69 static NTSTATUS xattr_tdb_push_attrs(TALLOC_CTX *mem_ctx,
70 const struct tdb_xattrs *attribs,
74 enum ndr_err_code ndr_err;
76 ndr_err = ndr_push_struct_blob(
77 &blob, mem_ctx, attribs,
78 (ndr_push_flags_fn_t)ndr_push_tdb_xattrs);
80 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
81 DEBUG(0, ("ndr_push_tdb_xattrs failed: %s\n",
82 ndr_errstr(ndr_err)));
83 return ndr_map_error2ntstatus(ndr_err);;
86 *data = make_tdb_data(blob.data, blob.length);
91 * Load tdb_xattrs for a file from the tdb
94 static NTSTATUS xattr_tdb_load_attrs(TALLOC_CTX *mem_ctx,
95 struct db_context *db_ctx,
96 const struct file_id *id,
97 struct tdb_xattrs **presult)
103 push_file_id_16((char *)id_buf, id);
105 if (db_ctx->fetch(db_ctx, mem_ctx,
106 make_tdb_data(id_buf, sizeof(id_buf)),
108 return NT_STATUS_INTERNAL_DB_CORRUPTION;
111 status = xattr_tdb_pull_attrs(mem_ctx, &data, presult);
112 TALLOC_FREE(data.dptr);
117 * fetch_lock the tdb_ea record for a file
120 static struct db_record *xattr_tdb_lock_attrs(TALLOC_CTX *mem_ctx,
121 struct db_context *db_ctx,
122 const struct file_id *id)
125 push_file_id_16((char *)id_buf, id);
126 return db_ctx->fetch_locked(db_ctx, mem_ctx,
127 make_tdb_data(id_buf, sizeof(id_buf)));
131 * Save tdb_xattrs to a previously fetch_locked record
134 static NTSTATUS xattr_tdb_save_attrs(struct db_record *rec,
135 const struct tdb_xattrs *attribs)
140 status = xattr_tdb_push_attrs(talloc_tos(), attribs, &data);
142 if (!NT_STATUS_IS_OK(status)) {
143 DEBUG(0, ("xattr_tdb_push_attrs failed: %s\n",
148 status = rec->store(rec, data, 0);
150 TALLOC_FREE(data.dptr);
156 * Worker routine for getxattr and fgetxattr
159 static ssize_t xattr_tdb_getattr(struct db_context *db_ctx,
160 const struct file_id *id,
161 const char *name, void *value, size_t size)
163 struct tdb_xattrs *attribs;
168 status = xattr_tdb_load_attrs(talloc_tos(), db_ctx, id, &attribs);
170 if (!NT_STATUS_IS_OK(status)) {
171 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
177 for (i=0; i<attribs->num_xattrs; i++) {
178 if (strcmp(attribs->xattrs[i].name, name) == 0) {
183 if (i == attribs->num_xattrs) {
188 if (attribs->xattrs[i].value.length > size) {
193 memcpy(value, attribs->xattrs[i].value.data,
194 attribs->xattrs[i].value.length);
195 result = attribs->xattrs[i].value.length;
198 TALLOC_FREE(attribs);
202 static ssize_t xattr_tdb_getxattr(struct vfs_handle_struct *handle,
203 const char *path, const char *name,
204 void *value, size_t size)
206 SMB_STRUCT_STAT sbuf;
208 struct db_context *db;
210 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
212 if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
216 id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
218 return xattr_tdb_getattr(db, &id, name, value, size);
221 static ssize_t xattr_tdb_fgetxattr(struct vfs_handle_struct *handle,
222 struct files_struct *fsp,
223 const char *name, void *value, size_t size)
225 SMB_STRUCT_STAT sbuf;
227 struct db_context *db;
229 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
231 if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
235 id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
237 return xattr_tdb_getattr(db, &id, name, value, size);
241 * Worker routine for setxattr and fsetxattr
244 static int xattr_tdb_setattr(struct db_context *db_ctx,
245 const struct file_id *id, const char *name,
246 const void *value, size_t size, int flags)
249 struct db_record *rec;
250 struct tdb_xattrs *attribs;
253 rec = xattr_tdb_lock_attrs(talloc_tos(), db_ctx, id);
256 DEBUG(0, ("xattr_tdb_lock_attrs failed\n"));
261 status = xattr_tdb_pull_attrs(rec, &rec->value, &attribs);
263 if (!NT_STATUS_IS_OK(status)) {
264 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
270 for (i=0; i<attribs->num_xattrs; i++) {
271 if (strcmp(attribs->xattrs[i].name, name) == 0) {
276 if (i == attribs->num_xattrs) {
277 struct tdb_xattr *tmp;
279 tmp = TALLOC_REALLOC_ARRAY(
280 attribs, attribs->xattrs, struct tdb_xattr,
281 attribs->num_xattrs + 1);
284 DEBUG(0, ("TALLOC_REALLOC_ARRAY failed\n"));
290 attribs->xattrs = tmp;
291 attribs->num_xattrs += 1;
294 attribs->xattrs[i].name = name;
295 attribs->xattrs[i].value.data = CONST_DISCARD(uint8 *, value);
296 attribs->xattrs[i].value.length = size;
298 status = xattr_tdb_save_attrs(rec, attribs);
302 if (!NT_STATUS_IS_OK(status)) {
303 DEBUG(1, ("save failed: %s\n", nt_errstr(status)));
310 static int xattr_tdb_setxattr(struct vfs_handle_struct *handle,
311 const char *path, const char *name,
312 const void *value, size_t size, int flags)
314 SMB_STRUCT_STAT sbuf;
316 struct db_context *db;
318 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
320 if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
324 id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
326 return xattr_tdb_setattr(db, &id, name, value, size, flags);
329 static int xattr_tdb_fsetxattr(struct vfs_handle_struct *handle,
330 struct files_struct *fsp,
331 const char *name, const void *value,
332 size_t size, int flags)
334 SMB_STRUCT_STAT sbuf;
336 struct db_context *db;
338 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
340 if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
344 id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
346 return xattr_tdb_setattr(db, &id, name, value, size, flags);
350 * Worker routine for listxattr and flistxattr
353 static ssize_t xattr_tdb_listattr(struct db_context *db_ctx,
354 const struct file_id *id, char *list,
358 struct tdb_xattrs *attribs;
362 status = xattr_tdb_load_attrs(talloc_tos(), db_ctx, id, &attribs);
364 if (!NT_STATUS_IS_OK(status)) {
365 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
371 DEBUG(10, ("xattr_tdb_listattr: Found %d xattrs\n",
372 attribs->num_xattrs));
374 for (i=0; i<attribs->num_xattrs; i++) {
377 DEBUG(10, ("xattr_tdb_listattr: xattrs[i].name: %s\n",
378 attribs->xattrs[i].name));
380 tmp = strlen(attribs->xattrs[i].name);
383 * Try to protect against overflow
386 if (len + (tmp+1) < len) {
387 TALLOC_FREE(attribs);
393 * Take care of the terminating NULL
399 TALLOC_FREE(attribs);
406 for (i=0; i<attribs->num_xattrs; i++) {
407 strlcpy(list+len, attribs->xattrs[i].name,
409 len += (strlen(attribs->xattrs[i].name) + 1);
412 TALLOC_FREE(attribs);
416 static ssize_t xattr_tdb_listxattr(struct vfs_handle_struct *handle,
417 const char *path, char *list, size_t size)
419 SMB_STRUCT_STAT sbuf;
421 struct db_context *db;
423 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
425 if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
429 id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
431 return xattr_tdb_listattr(db, &id, list, size);
434 static ssize_t xattr_tdb_flistxattr(struct vfs_handle_struct *handle,
435 struct files_struct *fsp, char *list,
438 SMB_STRUCT_STAT sbuf;
440 struct db_context *db;
442 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
444 if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
448 id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
450 return xattr_tdb_listattr(db, &id, list, size);
454 * Worker routine for removexattr and fremovexattr
457 static int xattr_tdb_removeattr(struct db_context *db_ctx,
458 const struct file_id *id, const char *name)
461 struct db_record *rec;
462 struct tdb_xattrs *attribs;
465 rec = xattr_tdb_lock_attrs(talloc_tos(), db_ctx, id);
468 DEBUG(0, ("xattr_tdb_lock_attrs failed\n"));
473 status = xattr_tdb_pull_attrs(rec, &rec->value, &attribs);
475 if (!NT_STATUS_IS_OK(status)) {
476 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
482 for (i=0; i<attribs->num_xattrs; i++) {
483 if (strcmp(attribs->xattrs[i].name, name) == 0) {
488 if (i == attribs->num_xattrs) {
495 attribs->xattrs[attribs->num_xattrs-1];
496 attribs->num_xattrs -= 1;
498 if (attribs->num_xattrs == 0) {
499 rec->delete_rec(rec);
504 status = xattr_tdb_save_attrs(rec, attribs);
508 if (!NT_STATUS_IS_OK(status)) {
509 DEBUG(1, ("save failed: %s\n", nt_errstr(status)));
516 static int xattr_tdb_removexattr(struct vfs_handle_struct *handle,
517 const char *path, const char *name)
519 SMB_STRUCT_STAT sbuf;
521 struct db_context *db;
523 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
525 if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
529 id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
531 return xattr_tdb_removeattr(db, &id, name);
534 static int xattr_tdb_fremovexattr(struct vfs_handle_struct *handle,
535 struct files_struct *fsp, const char *name)
537 SMB_STRUCT_STAT sbuf;
539 struct db_context *db;
541 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
543 if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
547 id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
549 return xattr_tdb_removeattr(db, &id, name);
553 * Open the tdb file upon VFS_CONNECT
556 static bool xattr_tdb_init(int snum, struct db_context **p_db)
558 struct db_context *db;
561 dbname = lp_parm_const_string(snum, "ea", "tdb", lock_path("eas.tdb"));
563 if (dbname == NULL) {
569 db = db_open(NULL, dbname, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
582 * On unlink we need to delete the tdb record
584 static int xattr_tdb_unlink(vfs_handle_struct *handle, const char *path)
586 SMB_STRUCT_STAT sbuf;
588 struct db_context *db;
589 struct db_record *rec;
592 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
594 if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
598 ret = SMB_VFS_NEXT_UNLINK(handle, path);
604 id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
606 rec = xattr_tdb_lock_attrs(talloc_tos(), db, &id);
609 * If rec == NULL there's not much we can do about it
613 rec->delete_rec(rec);
621 * On rmdir we need to delete the tdb record
623 static int xattr_tdb_rmdir(vfs_handle_struct *handle, const char *path)
625 SMB_STRUCT_STAT sbuf;
627 struct db_context *db;
628 struct db_record *rec;
631 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
633 if (SMB_VFS_STAT(handle->conn, path, &sbuf) == -1) {
637 ret = SMB_VFS_NEXT_RMDIR(handle, path);
643 id = SMB_VFS_FILE_ID_CREATE(handle->conn, sbuf.st_dev, sbuf.st_ino);
645 rec = xattr_tdb_lock_attrs(talloc_tos(), db, &id);
648 * If rec == NULL there's not much we can do about it
652 rec->delete_rec(rec);
660 * Destructor for the VFS private data
663 static void close_ea_db(void **data)
665 struct db_context **p_db = (struct db_context **)data;
669 static int xattr_tdb_connect(vfs_handle_struct *handle, const char *service,
674 struct db_context *db;
676 res = SMB_VFS_NEXT_CONNECT(handle, service, user);
681 fstrcpy(sname, service);
682 snum = find_service(sname);
685 * Should not happen, but we should not fail just *here*.
690 if (!xattr_tdb_init(snum, &db)) {
691 DEBUG(5, ("Could not init ea tdb\n"));
692 lp_do_parameter(snum, "ea support", "False");
696 lp_do_parameter(snum, "ea support", "True");
698 SMB_VFS_HANDLE_SET_DATA(handle, db, close_ea_db,
699 struct db_context, return -1);
704 /* VFS operations structure */
706 static const vfs_op_tuple xattr_tdb_ops[] = {
707 {SMB_VFS_OP(xattr_tdb_getxattr), SMB_VFS_OP_GETXATTR,
708 SMB_VFS_LAYER_TRANSPARENT},
709 {SMB_VFS_OP(xattr_tdb_fgetxattr), SMB_VFS_OP_FGETXATTR,
710 SMB_VFS_LAYER_TRANSPARENT},
711 {SMB_VFS_OP(xattr_tdb_setxattr), SMB_VFS_OP_SETXATTR,
712 SMB_VFS_LAYER_TRANSPARENT},
713 {SMB_VFS_OP(xattr_tdb_fsetxattr), SMB_VFS_OP_FSETXATTR,
714 SMB_VFS_LAYER_TRANSPARENT},
715 {SMB_VFS_OP(xattr_tdb_listxattr), SMB_VFS_OP_LISTXATTR,
716 SMB_VFS_LAYER_TRANSPARENT},
717 {SMB_VFS_OP(xattr_tdb_flistxattr), SMB_VFS_OP_FLISTXATTR,
718 SMB_VFS_LAYER_TRANSPARENT},
719 {SMB_VFS_OP(xattr_tdb_removexattr), SMB_VFS_OP_REMOVEXATTR,
720 SMB_VFS_LAYER_TRANSPARENT},
721 {SMB_VFS_OP(xattr_tdb_fremovexattr), SMB_VFS_OP_FREMOVEXATTR,
722 SMB_VFS_LAYER_TRANSPARENT},
723 {SMB_VFS_OP(xattr_tdb_unlink), SMB_VFS_OP_UNLINK,
724 SMB_VFS_LAYER_TRANSPARENT},
725 {SMB_VFS_OP(xattr_tdb_rmdir), SMB_VFS_OP_RMDIR,
726 SMB_VFS_LAYER_TRANSPARENT},
727 {SMB_VFS_OP(xattr_tdb_connect), SMB_VFS_OP_CONNECT,
728 SMB_VFS_LAYER_TRANSPARENT},
729 {SMB_VFS_OP(NULL), SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
732 NTSTATUS vfs_xattr_tdb_init(void);
733 NTSTATUS vfs_xattr_tdb_init(void)
735 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "xattr_tdb",