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 "system/filesys.h"
22 #include "librpc/gen_ndr/xattr.h"
23 #include "librpc/gen_ndr/ndr_xattr.h"
24 #include "../librpc/gen_ndr/ndr_netlogon.h"
28 #define DBGC_CLASS DBGC_VFS
31 * unmarshall tdb_xattrs
34 static NTSTATUS xattr_tdb_pull_attrs(TALLOC_CTX *mem_ctx,
36 struct tdb_xattrs **presult)
39 enum ndr_err_code ndr_err;
40 struct tdb_xattrs *result;
42 if (!(result = TALLOC_ZERO_P(mem_ctx, struct tdb_xattrs))) {
43 return NT_STATUS_NO_MEMORY;
46 if (data->dsize == 0) {
51 blob = data_blob_const(data->dptr, data->dsize);
53 ndr_err = ndr_pull_struct_blob(&blob, result, result,
54 (ndr_pull_flags_fn_t)ndr_pull_tdb_xattrs);
56 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
57 DEBUG(0, ("ndr_pull_tdb_xattrs failed: %s\n",
58 ndr_errstr(ndr_err)));
60 return ndr_map_error2ntstatus(ndr_err);
71 static NTSTATUS xattr_tdb_push_attrs(TALLOC_CTX *mem_ctx,
72 const struct tdb_xattrs *attribs,
76 enum ndr_err_code ndr_err;
78 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, attribs,
79 (ndr_push_flags_fn_t)ndr_push_tdb_xattrs);
81 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
82 DEBUG(0, ("ndr_push_tdb_xattrs failed: %s\n",
83 ndr_errstr(ndr_err)));
84 return ndr_map_error2ntstatus(ndr_err);
87 *data = make_tdb_data(blob.data, blob.length);
92 * Load tdb_xattrs for a file from the tdb
95 static NTSTATUS xattr_tdb_load_attrs(TALLOC_CTX *mem_ctx,
96 struct db_context *db_ctx,
97 const struct file_id *id,
98 struct tdb_xattrs **presult)
104 /* For backwards compatibility only store the dev/inode. */
105 push_file_id_16((char *)id_buf, id);
107 if (db_ctx->fetch(db_ctx, mem_ctx,
108 make_tdb_data(id_buf, sizeof(id_buf)),
110 return NT_STATUS_INTERNAL_DB_CORRUPTION;
113 status = xattr_tdb_pull_attrs(mem_ctx, &data, presult);
114 TALLOC_FREE(data.dptr);
119 * fetch_lock the tdb_ea record for a file
122 static struct db_record *xattr_tdb_lock_attrs(TALLOC_CTX *mem_ctx,
123 struct db_context *db_ctx,
124 const struct file_id *id)
128 /* For backwards compatibility only store the dev/inode. */
129 push_file_id_16((char *)id_buf, id);
130 return db_ctx->fetch_locked(db_ctx, mem_ctx,
131 make_tdb_data(id_buf, sizeof(id_buf)));
135 * Save tdb_xattrs to a previously fetch_locked record
138 static NTSTATUS xattr_tdb_save_attrs(struct db_record *rec,
139 const struct tdb_xattrs *attribs)
141 TDB_DATA data = tdb_null;
144 status = xattr_tdb_push_attrs(talloc_tos(), attribs, &data);
146 if (!NT_STATUS_IS_OK(status)) {
147 DEBUG(0, ("xattr_tdb_push_attrs failed: %s\n",
152 status = rec->store(rec, data, 0);
154 TALLOC_FREE(data.dptr);
160 * Worker routine for getxattr and fgetxattr
163 static ssize_t xattr_tdb_getattr(struct db_context *db_ctx,
164 const struct file_id *id,
165 const char *name, void *value, size_t size)
167 struct tdb_xattrs *attribs;
172 DEBUG(10, ("xattr_tdb_getattr called for file %s, name %s\n",
173 file_id_string_tos(id), name));
175 status = xattr_tdb_load_attrs(talloc_tos(), db_ctx, id, &attribs);
177 if (!NT_STATUS_IS_OK(status)) {
178 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
184 for (i=0; i<attribs->num_eas; i++) {
185 if (strcmp(attribs->eas[i].name, name) == 0) {
190 if (i == attribs->num_eas) {
195 if (attribs->eas[i].value.length > size) {
200 memcpy(value, attribs->eas[i].value.data,
201 attribs->eas[i].value.length);
202 result = attribs->eas[i].value.length;
205 TALLOC_FREE(attribs);
209 static ssize_t xattr_tdb_getxattr(struct vfs_handle_struct *handle,
210 const char *path, const char *name,
211 void *value, size_t size)
213 SMB_STRUCT_STAT sbuf;
215 struct db_context *db;
217 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
219 if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
223 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
225 return xattr_tdb_getattr(db, &id, name, value, size);
228 static ssize_t xattr_tdb_fgetxattr(struct vfs_handle_struct *handle,
229 struct files_struct *fsp,
230 const char *name, void *value, size_t size)
232 SMB_STRUCT_STAT sbuf;
234 struct db_context *db;
236 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
238 if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
242 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
244 return xattr_tdb_getattr(db, &id, name, value, size);
248 * Worker routine for setxattr and fsetxattr
251 static int xattr_tdb_setattr(struct db_context *db_ctx,
252 const struct file_id *id, const char *name,
253 const void *value, size_t size, int flags)
256 struct db_record *rec;
257 struct tdb_xattrs *attribs;
260 DEBUG(10, ("xattr_tdb_setattr called for file %s, name %s\n",
261 file_id_string_tos(id), name));
263 rec = xattr_tdb_lock_attrs(talloc_tos(), db_ctx, id);
266 DEBUG(0, ("xattr_tdb_lock_attrs failed\n"));
271 status = xattr_tdb_pull_attrs(rec, &rec->value, &attribs);
273 if (!NT_STATUS_IS_OK(status)) {
274 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
280 for (i=0; i<attribs->num_eas; i++) {
281 if (strcmp(attribs->eas[i].name, name) == 0) {
282 if (flags & XATTR_CREATE) {
291 if (i == attribs->num_eas) {
292 struct xattr_EA *tmp;
294 if (flags & XATTR_REPLACE) {
300 tmp = TALLOC_REALLOC_ARRAY(
301 attribs, attribs->eas, struct xattr_EA,
302 attribs->num_eas+ 1);
305 DEBUG(0, ("TALLOC_REALLOC_ARRAY failed\n"));
312 attribs->num_eas += 1;
315 attribs->eas[i].name = name;
316 attribs->eas[i].value.data = CONST_DISCARD(uint8 *, value);
317 attribs->eas[i].value.length = size;
319 status = xattr_tdb_save_attrs(rec, attribs);
323 if (!NT_STATUS_IS_OK(status)) {
324 DEBUG(1, ("save failed: %s\n", nt_errstr(status)));
331 static int xattr_tdb_setxattr(struct vfs_handle_struct *handle,
332 const char *path, const char *name,
333 const void *value, size_t size, int flags)
335 SMB_STRUCT_STAT sbuf;
337 struct db_context *db;
339 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
341 if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
345 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
347 return xattr_tdb_setattr(db, &id, name, value, size, flags);
350 static int xattr_tdb_fsetxattr(struct vfs_handle_struct *handle,
351 struct files_struct *fsp,
352 const char *name, const void *value,
353 size_t size, int flags)
355 SMB_STRUCT_STAT sbuf;
357 struct db_context *db;
359 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
361 if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
365 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
367 return xattr_tdb_setattr(db, &id, name, value, size, flags);
371 * Worker routine for listxattr and flistxattr
374 static ssize_t xattr_tdb_listattr(struct db_context *db_ctx,
375 const struct file_id *id, char *list,
379 struct tdb_xattrs *attribs;
383 status = xattr_tdb_load_attrs(talloc_tos(), db_ctx, id, &attribs);
385 if (!NT_STATUS_IS_OK(status)) {
386 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
392 DEBUG(10, ("xattr_tdb_listattr: Found %d xattrs\n",
395 for (i=0; i<attribs->num_eas; i++) {
398 DEBUG(10, ("xattr_tdb_listattr: xattrs[i].name: %s\n",
399 attribs->eas[i].name));
401 tmp = strlen(attribs->eas[i].name);
404 * Try to protect against overflow
407 if (len + (tmp+1) < len) {
408 TALLOC_FREE(attribs);
414 * Take care of the terminating NULL
420 TALLOC_FREE(attribs);
427 for (i=0; i<attribs->num_eas; i++) {
428 strlcpy(list+len, attribs->eas[i].name,
430 len += (strlen(attribs->eas[i].name) + 1);
433 TALLOC_FREE(attribs);
437 static ssize_t xattr_tdb_listxattr(struct vfs_handle_struct *handle,
438 const char *path, char *list, size_t size)
440 SMB_STRUCT_STAT sbuf;
442 struct db_context *db;
444 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
446 if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
450 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
452 return xattr_tdb_listattr(db, &id, list, size);
455 static ssize_t xattr_tdb_flistxattr(struct vfs_handle_struct *handle,
456 struct files_struct *fsp, char *list,
459 SMB_STRUCT_STAT sbuf;
461 struct db_context *db;
463 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
465 if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
469 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
471 return xattr_tdb_listattr(db, &id, list, size);
475 * Worker routine for removexattr and fremovexattr
478 static int xattr_tdb_removeattr(struct db_context *db_ctx,
479 const struct file_id *id, const char *name)
482 struct db_record *rec;
483 struct tdb_xattrs *attribs;
486 rec = xattr_tdb_lock_attrs(talloc_tos(), db_ctx, id);
489 DEBUG(0, ("xattr_tdb_lock_attrs failed\n"));
494 status = xattr_tdb_pull_attrs(rec, &rec->value, &attribs);
496 if (!NT_STATUS_IS_OK(status)) {
497 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
503 for (i=0; i<attribs->num_eas; i++) {
504 if (strcmp(attribs->eas[i].name, name) == 0) {
509 if (i == attribs->num_eas) {
516 attribs->eas[attribs->num_eas-1];
517 attribs->num_eas -= 1;
519 if (attribs->num_eas == 0) {
520 rec->delete_rec(rec);
525 status = xattr_tdb_save_attrs(rec, attribs);
529 if (!NT_STATUS_IS_OK(status)) {
530 DEBUG(1, ("save failed: %s\n", nt_errstr(status)));
537 static int xattr_tdb_removexattr(struct vfs_handle_struct *handle,
538 const char *path, const char *name)
540 SMB_STRUCT_STAT sbuf;
542 struct db_context *db;
544 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
546 if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
550 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
552 return xattr_tdb_removeattr(db, &id, name);
555 static int xattr_tdb_fremovexattr(struct vfs_handle_struct *handle,
556 struct files_struct *fsp, const char *name)
558 SMB_STRUCT_STAT sbuf;
560 struct db_context *db;
562 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
564 if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
568 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
570 return xattr_tdb_removeattr(db, &id, name);
574 * Open the tdb file upon VFS_CONNECT
577 static bool xattr_tdb_init(int snum, struct db_context **p_db)
579 struct db_context *db;
583 def_dbname = state_path("xattr.tdb");
584 if (def_dbname == NULL) {
589 dbname = lp_parm_const_string(snum, "xattr_tdb", "file", def_dbname);
591 /* now we know dbname is not NULL */
594 db = db_open(NULL, dbname, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
603 TALLOC_FREE(def_dbname);
608 TALLOC_FREE(def_dbname);
613 * On unlink we need to delete the tdb record
615 static int xattr_tdb_unlink(vfs_handle_struct *handle,
616 const struct smb_filename *smb_fname)
618 struct smb_filename *smb_fname_tmp = NULL;
620 struct db_context *db;
621 struct db_record *rec;
624 bool remove_record = false;
626 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
628 status = copy_smb_filename(talloc_tos(), smb_fname, &smb_fname_tmp);
629 if (!NT_STATUS_IS_OK(status)) {
630 errno = map_errno_from_nt_status(status);
634 if (lp_posix_pathnames()) {
635 ret = SMB_VFS_LSTAT(handle->conn, smb_fname_tmp);
637 ret = SMB_VFS_STAT(handle->conn, smb_fname_tmp);
643 if (smb_fname_tmp->st.st_ex_nlink == 1) {
644 /* Only remove record on last link to file. */
645 remove_record = true;
648 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname_tmp);
654 if (!remove_record) {
658 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &smb_fname_tmp->st);
660 rec = xattr_tdb_lock_attrs(talloc_tos(), db, &id);
663 * If rec == NULL there's not much we can do about it
667 rec->delete_rec(rec);
672 TALLOC_FREE(smb_fname_tmp);
677 * On rmdir we need to delete the tdb record
679 static int xattr_tdb_rmdir(vfs_handle_struct *handle, const char *path)
681 SMB_STRUCT_STAT sbuf;
683 struct db_context *db;
684 struct db_record *rec;
687 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
689 if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
693 ret = SMB_VFS_NEXT_RMDIR(handle, path);
699 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
701 rec = xattr_tdb_lock_attrs(talloc_tos(), db, &id);
704 * If rec == NULL there's not much we can do about it
708 rec->delete_rec(rec);
716 * Destructor for the VFS private data
719 static void close_xattr_db(void **data)
721 struct db_context **p_db = (struct db_context **)data;
725 static int xattr_tdb_connect(vfs_handle_struct *handle, const char *service,
730 struct db_context *db;
732 res = SMB_VFS_NEXT_CONNECT(handle, service, user);
737 snum = find_service(talloc_tos(), service, &sname);
738 if (snum == -1 || sname == NULL) {
740 * Should not happen, but we should not fail just *here*.
745 if (!xattr_tdb_init(snum, &db)) {
746 DEBUG(5, ("Could not init xattr tdb\n"));
747 lp_do_parameter(snum, "ea support", "False");
751 lp_do_parameter(snum, "ea support", "True");
753 SMB_VFS_HANDLE_SET_DATA(handle, db, close_xattr_db,
754 struct db_context, return -1);
759 static struct vfs_fn_pointers vfs_xattr_tdb_fns = {
760 .getxattr = xattr_tdb_getxattr,
761 .fgetxattr = xattr_tdb_fgetxattr,
762 .setxattr = xattr_tdb_setxattr,
763 .fsetxattr = xattr_tdb_fsetxattr,
764 .listxattr = xattr_tdb_listxattr,
765 .flistxattr = xattr_tdb_flistxattr,
766 .removexattr = xattr_tdb_removexattr,
767 .fremovexattr = xattr_tdb_fremovexattr,
768 .unlink = xattr_tdb_unlink,
769 .rmdir = xattr_tdb_rmdir,
770 .connect_fn = xattr_tdb_connect,
773 NTSTATUS vfs_xattr_tdb_init(void);
774 NTSTATUS vfs_xattr_tdb_init(void)
776 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "xattr_tdb",