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"
23 #include "../librpc/gen_ndr/ndr_netlogon.h"
26 #define DBGC_CLASS DBGC_VFS
29 * unmarshall tdb_xattrs
32 static NTSTATUS xattr_tdb_pull_attrs(TALLOC_CTX *mem_ctx,
34 struct tdb_xattrs **presult)
37 enum ndr_err_code ndr_err;
38 struct tdb_xattrs *result;
40 if (!(result = TALLOC_ZERO_P(mem_ctx, struct tdb_xattrs))) {
41 return NT_STATUS_NO_MEMORY;
44 if (data->dsize == 0) {
49 blob = data_blob_const(data->dptr, data->dsize);
51 ndr_err = ndr_pull_struct_blob(&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(&blob, mem_ctx, attribs,
77 (ndr_push_flags_fn_t)ndr_push_tdb_xattrs);
79 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
80 DEBUG(0, ("ndr_push_tdb_xattrs failed: %s\n",
81 ndr_errstr(ndr_err)));
82 return ndr_map_error2ntstatus(ndr_err);;
85 *data = make_tdb_data(blob.data, blob.length);
90 * Load tdb_xattrs for a file from the tdb
93 static NTSTATUS xattr_tdb_load_attrs(TALLOC_CTX *mem_ctx,
94 struct db_context *db_ctx,
95 const struct file_id *id,
96 struct tdb_xattrs **presult)
102 /* For backwards compatibility only store the dev/inode. */
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)
126 /* For backwards compatibility only store the dev/inode. */
127 push_file_id_16((char *)id_buf, id);
128 return db_ctx->fetch_locked(db_ctx, mem_ctx,
129 make_tdb_data(id_buf, sizeof(id_buf)));
133 * Save tdb_xattrs to a previously fetch_locked record
136 static NTSTATUS xattr_tdb_save_attrs(struct db_record *rec,
137 const struct tdb_xattrs *attribs)
139 TDB_DATA data = tdb_null;
142 status = xattr_tdb_push_attrs(talloc_tos(), attribs, &data);
144 if (!NT_STATUS_IS_OK(status)) {
145 DEBUG(0, ("xattr_tdb_push_attrs failed: %s\n",
150 status = rec->store(rec, data, 0);
152 TALLOC_FREE(data.dptr);
158 * Worker routine for getxattr and fgetxattr
161 static ssize_t xattr_tdb_getattr(struct db_context *db_ctx,
162 const struct file_id *id,
163 const char *name, void *value, size_t size)
165 struct tdb_xattrs *attribs;
170 DEBUG(10, ("xattr_tdb_getattr called for file %s, name %s\n",
171 file_id_string_tos(id), name));
173 status = xattr_tdb_load_attrs(talloc_tos(), db_ctx, id, &attribs);
175 if (!NT_STATUS_IS_OK(status)) {
176 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
182 for (i=0; i<attribs->num_eas; i++) {
183 if (strcmp(attribs->eas[i].name, name) == 0) {
188 if (i == attribs->num_eas) {
193 if (attribs->eas[i].value.length > size) {
198 memcpy(value, attribs->eas[i].value.data,
199 attribs->eas[i].value.length);
200 result = attribs->eas[i].value.length;
203 TALLOC_FREE(attribs);
207 static ssize_t xattr_tdb_getxattr(struct vfs_handle_struct *handle,
208 const char *path, const char *name,
209 void *value, size_t size)
211 SMB_STRUCT_STAT sbuf;
213 struct db_context *db;
215 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
217 if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
221 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
223 return xattr_tdb_getattr(db, &id, name, value, size);
226 static ssize_t xattr_tdb_fgetxattr(struct vfs_handle_struct *handle,
227 struct files_struct *fsp,
228 const char *name, void *value, size_t size)
230 SMB_STRUCT_STAT sbuf;
232 struct db_context *db;
234 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
236 if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
240 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
242 return xattr_tdb_getattr(db, &id, name, value, size);
246 * Worker routine for setxattr and fsetxattr
249 static int xattr_tdb_setattr(struct db_context *db_ctx,
250 const struct file_id *id, const char *name,
251 const void *value, size_t size, int flags)
254 struct db_record *rec;
255 struct tdb_xattrs *attribs;
258 DEBUG(10, ("xattr_tdb_setattr called for file %s, name %s\n",
259 file_id_string_tos(id), name));
261 rec = xattr_tdb_lock_attrs(talloc_tos(), db_ctx, id);
264 DEBUG(0, ("xattr_tdb_lock_attrs failed\n"));
269 status = xattr_tdb_pull_attrs(rec, &rec->value, &attribs);
271 if (!NT_STATUS_IS_OK(status)) {
272 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
278 for (i=0; i<attribs->num_eas; i++) {
279 if (strcmp(attribs->eas[i].name, name) == 0) {
280 if (flags & XATTR_CREATE) {
289 if (i == attribs->num_eas) {
290 struct xattr_EA *tmp;
292 if (flags & XATTR_REPLACE) {
298 tmp = TALLOC_REALLOC_ARRAY(
299 attribs, attribs->eas, struct xattr_EA,
300 attribs->num_eas+ 1);
303 DEBUG(0, ("TALLOC_REALLOC_ARRAY failed\n"));
310 attribs->num_eas += 1;
313 attribs->eas[i].name = name;
314 attribs->eas[i].value.data = CONST_DISCARD(uint8 *, value);
315 attribs->eas[i].value.length = size;
317 status = xattr_tdb_save_attrs(rec, attribs);
321 if (!NT_STATUS_IS_OK(status)) {
322 DEBUG(1, ("save failed: %s\n", nt_errstr(status)));
329 static int xattr_tdb_setxattr(struct vfs_handle_struct *handle,
330 const char *path, const char *name,
331 const void *value, size_t size, int flags)
333 SMB_STRUCT_STAT sbuf;
335 struct db_context *db;
337 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
339 if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
343 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
345 return xattr_tdb_setattr(db, &id, name, value, size, flags);
348 static int xattr_tdb_fsetxattr(struct vfs_handle_struct *handle,
349 struct files_struct *fsp,
350 const char *name, const void *value,
351 size_t size, int flags)
353 SMB_STRUCT_STAT sbuf;
355 struct db_context *db;
357 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
359 if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
363 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
365 return xattr_tdb_setattr(db, &id, name, value, size, flags);
369 * Worker routine for listxattr and flistxattr
372 static ssize_t xattr_tdb_listattr(struct db_context *db_ctx,
373 const struct file_id *id, char *list,
377 struct tdb_xattrs *attribs;
381 status = xattr_tdb_load_attrs(talloc_tos(), db_ctx, id, &attribs);
383 if (!NT_STATUS_IS_OK(status)) {
384 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
390 DEBUG(10, ("xattr_tdb_listattr: Found %d xattrs\n",
393 for (i=0; i<attribs->num_eas; i++) {
396 DEBUG(10, ("xattr_tdb_listattr: xattrs[i].name: %s\n",
397 attribs->eas[i].name));
399 tmp = strlen(attribs->eas[i].name);
402 * Try to protect against overflow
405 if (len + (tmp+1) < len) {
406 TALLOC_FREE(attribs);
412 * Take care of the terminating NULL
418 TALLOC_FREE(attribs);
425 for (i=0; i<attribs->num_eas; i++) {
426 strlcpy(list+len, attribs->eas[i].name,
428 len += (strlen(attribs->eas[i].name) + 1);
431 TALLOC_FREE(attribs);
435 static ssize_t xattr_tdb_listxattr(struct vfs_handle_struct *handle,
436 const char *path, char *list, size_t size)
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 (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
448 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
450 return xattr_tdb_listattr(db, &id, list, size);
453 static ssize_t xattr_tdb_flistxattr(struct vfs_handle_struct *handle,
454 struct files_struct *fsp, char *list,
457 SMB_STRUCT_STAT sbuf;
459 struct db_context *db;
461 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
463 if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
467 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
469 return xattr_tdb_listattr(db, &id, list, size);
473 * Worker routine for removexattr and fremovexattr
476 static int xattr_tdb_removeattr(struct db_context *db_ctx,
477 const struct file_id *id, const char *name)
480 struct db_record *rec;
481 struct tdb_xattrs *attribs;
484 rec = xattr_tdb_lock_attrs(talloc_tos(), db_ctx, id);
487 DEBUG(0, ("xattr_tdb_lock_attrs failed\n"));
492 status = xattr_tdb_pull_attrs(rec, &rec->value, &attribs);
494 if (!NT_STATUS_IS_OK(status)) {
495 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
501 for (i=0; i<attribs->num_eas; i++) {
502 if (strcmp(attribs->eas[i].name, name) == 0) {
507 if (i == attribs->num_eas) {
514 attribs->eas[attribs->num_eas-1];
515 attribs->num_eas -= 1;
517 if (attribs->num_eas == 0) {
518 rec->delete_rec(rec);
523 status = xattr_tdb_save_attrs(rec, attribs);
527 if (!NT_STATUS_IS_OK(status)) {
528 DEBUG(1, ("save failed: %s\n", nt_errstr(status)));
535 static int xattr_tdb_removexattr(struct vfs_handle_struct *handle,
536 const char *path, const char *name)
538 SMB_STRUCT_STAT sbuf;
540 struct db_context *db;
542 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
544 if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
548 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
550 return xattr_tdb_removeattr(db, &id, name);
553 static int xattr_tdb_fremovexattr(struct vfs_handle_struct *handle,
554 struct files_struct *fsp, const char *name)
556 SMB_STRUCT_STAT sbuf;
558 struct db_context *db;
560 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
562 if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
566 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
568 return xattr_tdb_removeattr(db, &id, name);
572 * Open the tdb file upon VFS_CONNECT
575 static bool xattr_tdb_init(int snum, struct db_context **p_db)
577 struct db_context *db;
581 def_dbname = state_path("xattr.tdb");
582 if (def_dbname == NULL) {
587 dbname = lp_parm_const_string(snum, "xattr_tdb", "file", def_dbname);
589 /* now we know dbname is not NULL */
592 db = db_open(NULL, dbname, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
601 TALLOC_FREE(def_dbname);
606 TALLOC_FREE(def_dbname);
611 * On unlink we need to delete the tdb record
613 static int xattr_tdb_unlink(vfs_handle_struct *handle,
614 const struct smb_filename *smb_fname)
616 struct smb_filename *smb_fname_tmp = NULL;
618 struct db_context *db;
619 struct db_record *rec;
622 bool remove_record = false;
624 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
626 status = copy_smb_filename(talloc_tos(), smb_fname, &smb_fname_tmp);
627 if (!NT_STATUS_IS_OK(status)) {
628 errno = map_errno_from_nt_status(status);
632 if (lp_posix_pathnames()) {
633 ret = SMB_VFS_LSTAT(handle->conn, smb_fname_tmp);
635 ret = SMB_VFS_STAT(handle->conn, smb_fname_tmp);
641 if (smb_fname_tmp->st.st_ex_nlink == 1) {
642 /* Only remove record on last link to file. */
643 remove_record = true;
646 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname_tmp);
652 if (!remove_record) {
656 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &smb_fname_tmp->st);
658 rec = xattr_tdb_lock_attrs(talloc_tos(), db, &id);
661 * If rec == NULL there's not much we can do about it
665 rec->delete_rec(rec);
670 TALLOC_FREE(smb_fname_tmp);
675 * On rmdir we need to delete the tdb record
677 static int xattr_tdb_rmdir(vfs_handle_struct *handle, const char *path)
679 SMB_STRUCT_STAT sbuf;
681 struct db_context *db;
682 struct db_record *rec;
685 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
687 if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
691 ret = SMB_VFS_NEXT_RMDIR(handle, path);
697 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
699 rec = xattr_tdb_lock_attrs(talloc_tos(), db, &id);
702 * If rec == NULL there's not much we can do about it
706 rec->delete_rec(rec);
714 * Destructor for the VFS private data
717 static void close_xattr_db(void **data)
719 struct db_context **p_db = (struct db_context **)data;
723 static int xattr_tdb_connect(vfs_handle_struct *handle, const char *service,
728 struct db_context *db;
730 res = SMB_VFS_NEXT_CONNECT(handle, service, user);
735 fstrcpy(sname, service);
736 snum = find_service(sname);
739 * Should not happen, but we should not fail just *here*.
744 if (!xattr_tdb_init(snum, &db)) {
745 DEBUG(5, ("Could not init xattr tdb\n"));
746 lp_do_parameter(snum, "ea support", "False");
750 lp_do_parameter(snum, "ea support", "True");
752 SMB_VFS_HANDLE_SET_DATA(handle, db, close_xattr_db,
753 struct db_context, return -1);
758 static struct vfs_fn_pointers vfs_xattr_tdb_fns = {
759 .getxattr = xattr_tdb_getxattr,
760 .fgetxattr = xattr_tdb_fgetxattr,
761 .setxattr = xattr_tdb_setxattr,
762 .fsetxattr = xattr_tdb_fsetxattr,
763 .listxattr = xattr_tdb_listxattr,
764 .flistxattr = xattr_tdb_flistxattr,
765 .removexattr = xattr_tdb_removexattr,
766 .fremovexattr = xattr_tdb_fremovexattr,
767 .unlink = xattr_tdb_unlink,
768 .rmdir = xattr_tdb_rmdir,
769 .connect_fn = xattr_tdb_connect,
772 NTSTATUS vfs_xattr_tdb_init(void);
773 NTSTATUS vfs_xattr_tdb_init(void)
775 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "xattr_tdb",