s3-xattr_tdb: Fix listxattr
[Samba/gebeck_regimport.git] / source3 / modules / vfs_xattr_tdb.c
blob1f3f20e4993042509bc49fc7ac9bb1b58a71ea9c
1 /*
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/>.
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/dbwrap.h"
27 #include "dbwrap/dbwrap_open.h"
28 #include "util_tdb.h"
30 #undef DBGC_CLASS
31 #define DBGC_CLASS DBGC_VFS
34 * unmarshall tdb_xattrs
37 static NTSTATUS xattr_tdb_pull_attrs(TALLOC_CTX *mem_ctx,
38 const TDB_DATA *data,
39 struct tdb_xattrs **presult)
41 DATA_BLOB blob;
42 enum ndr_err_code ndr_err;
43 struct tdb_xattrs *result;
45 if (!(result = talloc_zero(mem_ctx, struct tdb_xattrs))) {
46 return NT_STATUS_NO_MEMORY;
49 if (data->dsize == 0) {
50 *presult = result;
51 return NT_STATUS_OK;
54 blob = data_blob_const(data->dptr, data->dsize);
56 ndr_err = ndr_pull_struct_blob(&blob, result, result,
57 (ndr_pull_flags_fn_t)ndr_pull_tdb_xattrs);
59 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
60 DEBUG(0, ("ndr_pull_tdb_xattrs failed: %s\n",
61 ndr_errstr(ndr_err)));
62 TALLOC_FREE(result);
63 return ndr_map_error2ntstatus(ndr_err);
66 *presult = result;
67 return NT_STATUS_OK;
71 * marshall tdb_xattrs
74 static NTSTATUS xattr_tdb_push_attrs(TALLOC_CTX *mem_ctx,
75 const struct tdb_xattrs *attribs,
76 TDB_DATA *data)
78 DATA_BLOB blob;
79 enum ndr_err_code ndr_err;
81 ndr_err = ndr_push_struct_blob(&blob, mem_ctx, attribs,
82 (ndr_push_flags_fn_t)ndr_push_tdb_xattrs);
84 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
85 DEBUG(0, ("ndr_push_tdb_xattrs failed: %s\n",
86 ndr_errstr(ndr_err)));
87 return ndr_map_error2ntstatus(ndr_err);
90 *data = make_tdb_data(blob.data, blob.length);
91 return NT_STATUS_OK;
95 * Load tdb_xattrs for a file from the tdb
98 static NTSTATUS xattr_tdb_load_attrs(TALLOC_CTX *mem_ctx,
99 struct db_context *db_ctx,
100 const struct file_id *id,
101 struct tdb_xattrs **presult)
103 uint8 id_buf[16];
104 NTSTATUS status;
105 TDB_DATA data;
107 /* For backwards compatibility only store the dev/inode. */
108 push_file_id_16((char *)id_buf, id);
110 status = dbwrap_fetch(db_ctx, mem_ctx,
111 make_tdb_data(id_buf, sizeof(id_buf)),
112 &data);
113 if (!NT_STATUS_IS_OK(status)) {
114 return NT_STATUS_INTERNAL_DB_CORRUPTION;
117 status = xattr_tdb_pull_attrs(mem_ctx, &data, presult);
118 TALLOC_FREE(data.dptr);
119 return status;
123 * fetch_lock the tdb_ea record for a file
126 static struct db_record *xattr_tdb_lock_attrs(TALLOC_CTX *mem_ctx,
127 struct db_context *db_ctx,
128 const struct file_id *id)
130 uint8 id_buf[16];
132 /* For backwards compatibility only store the dev/inode. */
133 push_file_id_16((char *)id_buf, id);
134 return dbwrap_fetch_locked(db_ctx, mem_ctx,
135 make_tdb_data(id_buf, sizeof(id_buf)));
139 * Save tdb_xattrs to a previously fetch_locked record
142 static NTSTATUS xattr_tdb_save_attrs(struct db_record *rec,
143 const struct tdb_xattrs *attribs)
145 TDB_DATA data = tdb_null;
146 NTSTATUS status;
148 status = xattr_tdb_push_attrs(talloc_tos(), attribs, &data);
150 if (!NT_STATUS_IS_OK(status)) {
151 DEBUG(0, ("xattr_tdb_push_attrs failed: %s\n",
152 nt_errstr(status)));
153 return status;
156 status = dbwrap_record_store(rec, data, 0);
158 TALLOC_FREE(data.dptr);
160 return status;
164 * Worker routine for getxattr and fgetxattr
167 static ssize_t xattr_tdb_getattr(struct db_context *db_ctx,
168 const struct file_id *id,
169 const char *name, void *value, size_t size)
171 struct tdb_xattrs *attribs;
172 uint32_t i;
173 ssize_t result = -1;
174 NTSTATUS status;
176 DEBUG(10, ("xattr_tdb_getattr called for file %s, name %s\n",
177 file_id_string_tos(id), name));
179 status = xattr_tdb_load_attrs(talloc_tos(), db_ctx, id, &attribs);
181 if (!NT_STATUS_IS_OK(status)) {
182 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
183 nt_errstr(status)));
184 errno = EINVAL;
185 return -1;
188 for (i=0; i<attribs->num_eas; i++) {
189 if (strcmp(attribs->eas[i].name, name) == 0) {
190 break;
194 if (i == attribs->num_eas) {
195 errno = ENOATTR;
196 goto fail;
199 if (attribs->eas[i].value.length > size) {
200 errno = ERANGE;
201 goto fail;
204 memcpy(value, attribs->eas[i].value.data,
205 attribs->eas[i].value.length);
206 result = attribs->eas[i].value.length;
208 fail:
209 TALLOC_FREE(attribs);
210 return result;
213 static ssize_t xattr_tdb_getxattr(struct vfs_handle_struct *handle,
214 const char *path, const char *name,
215 void *value, size_t size)
217 SMB_STRUCT_STAT sbuf;
218 struct file_id id;
219 struct db_context *db;
221 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
223 if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
224 return -1;
227 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
229 return xattr_tdb_getattr(db, &id, name, value, size);
232 static ssize_t xattr_tdb_fgetxattr(struct vfs_handle_struct *handle,
233 struct files_struct *fsp,
234 const char *name, void *value, size_t size)
236 SMB_STRUCT_STAT sbuf;
237 struct file_id id;
238 struct db_context *db;
240 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
242 if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
243 return -1;
246 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
248 return xattr_tdb_getattr(db, &id, name, value, size);
252 * Worker routine for setxattr and fsetxattr
255 static int xattr_tdb_setattr(struct db_context *db_ctx,
256 const struct file_id *id, const char *name,
257 const void *value, size_t size, int flags)
259 NTSTATUS status;
260 struct db_record *rec;
261 struct tdb_xattrs *attribs;
262 uint32_t i;
263 TDB_DATA data;
265 DEBUG(10, ("xattr_tdb_setattr called for file %s, name %s\n",
266 file_id_string_tos(id), name));
268 rec = xattr_tdb_lock_attrs(talloc_tos(), db_ctx, id);
270 if (rec == NULL) {
271 DEBUG(0, ("xattr_tdb_lock_attrs failed\n"));
272 errno = EINVAL;
273 return -1;
276 data = dbwrap_record_get_value(rec);
278 status = xattr_tdb_pull_attrs(rec, &data, &attribs);
280 if (!NT_STATUS_IS_OK(status)) {
281 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
282 nt_errstr(status)));
283 TALLOC_FREE(rec);
284 return -1;
287 for (i=0; i<attribs->num_eas; i++) {
288 if (strcmp(attribs->eas[i].name, name) == 0) {
289 if (flags & XATTR_CREATE) {
290 TALLOC_FREE(rec);
291 errno = EEXIST;
292 return -1;
294 break;
298 if (i == attribs->num_eas) {
299 struct xattr_EA *tmp;
301 if (flags & XATTR_REPLACE) {
302 TALLOC_FREE(rec);
303 errno = ENOATTR;
304 return -1;
307 tmp = talloc_realloc(
308 attribs, attribs->eas, struct xattr_EA,
309 attribs->num_eas+ 1);
311 if (tmp == NULL) {
312 DEBUG(0, ("talloc_realloc failed\n"));
313 TALLOC_FREE(rec);
314 errno = ENOMEM;
315 return -1;
318 attribs->eas = tmp;
319 attribs->num_eas += 1;
322 attribs->eas[i].name = name;
323 attribs->eas[i].value.data = discard_const_p(uint8, value);
324 attribs->eas[i].value.length = size;
326 status = xattr_tdb_save_attrs(rec, attribs);
328 TALLOC_FREE(rec);
330 if (!NT_STATUS_IS_OK(status)) {
331 DEBUG(1, ("save failed: %s\n", nt_errstr(status)));
332 return -1;
335 return 0;
338 static int xattr_tdb_setxattr(struct vfs_handle_struct *handle,
339 const char *path, const char *name,
340 const void *value, size_t size, int flags)
342 SMB_STRUCT_STAT sbuf;
343 struct file_id id;
344 struct db_context *db;
346 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
348 if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
349 return -1;
352 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
354 return xattr_tdb_setattr(db, &id, name, value, size, flags);
357 static int xattr_tdb_fsetxattr(struct vfs_handle_struct *handle,
358 struct files_struct *fsp,
359 const char *name, const void *value,
360 size_t size, int flags)
362 SMB_STRUCT_STAT sbuf;
363 struct file_id id;
364 struct db_context *db;
366 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
368 if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
369 return -1;
372 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
374 return xattr_tdb_setattr(db, &id, name, value, size, flags);
378 * Worker routine for listxattr and flistxattr
381 static ssize_t xattr_tdb_listattr(struct db_context *db_ctx,
382 const struct file_id *id, char *list,
383 size_t size)
385 NTSTATUS status;
386 struct tdb_xattrs *attribs;
387 uint32_t i;
388 size_t len = 0;
390 status = xattr_tdb_load_attrs(talloc_tos(), db_ctx, id, &attribs);
392 if (!NT_STATUS_IS_OK(status)) {
393 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
394 nt_errstr(status)));
395 errno = EINVAL;
396 return -1;
399 DEBUG(10, ("xattr_tdb_listattr: Found %d xattrs\n",
400 attribs->num_eas));
402 for (i=0; i<attribs->num_eas; i++) {
403 size_t tmp;
405 DEBUG(10, ("xattr_tdb_listattr: xattrs[i].name: %s\n",
406 attribs->eas[i].name));
408 tmp = strlen(attribs->eas[i].name);
411 * Try to protect against overflow
414 if (len + (tmp+1) < len) {
415 TALLOC_FREE(attribs);
416 errno = EINVAL;
417 return -1;
421 * Take care of the terminating NULL
423 len += (tmp + 1);
426 if (len > size) {
427 TALLOC_FREE(attribs);
428 errno = ERANGE;
429 return len;
432 len = 0;
434 for (i=0; i<attribs->num_eas; i++) {
435 strlcpy(list+len, attribs->eas[i].name,
436 size-len);
437 len += (strlen(attribs->eas[i].name) + 1);
440 TALLOC_FREE(attribs);
441 return len;
444 static ssize_t xattr_tdb_listxattr(struct vfs_handle_struct *handle,
445 const char *path, char *list, size_t size)
447 SMB_STRUCT_STAT sbuf;
448 struct file_id id;
449 struct db_context *db;
451 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
453 if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
454 return -1;
457 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
459 return xattr_tdb_listattr(db, &id, list, size);
462 static ssize_t xattr_tdb_flistxattr(struct vfs_handle_struct *handle,
463 struct files_struct *fsp, char *list,
464 size_t size)
466 SMB_STRUCT_STAT sbuf;
467 struct file_id id;
468 struct db_context *db;
470 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
472 if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
473 return -1;
476 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
478 return xattr_tdb_listattr(db, &id, list, size);
482 * Worker routine for removexattr and fremovexattr
485 static int xattr_tdb_removeattr(struct db_context *db_ctx,
486 const struct file_id *id, const char *name)
488 NTSTATUS status;
489 struct db_record *rec;
490 struct tdb_xattrs *attribs;
491 uint32_t i;
492 TDB_DATA value;
494 rec = xattr_tdb_lock_attrs(talloc_tos(), db_ctx, id);
496 if (rec == NULL) {
497 DEBUG(0, ("xattr_tdb_lock_attrs failed\n"));
498 errno = EINVAL;
499 return -1;
502 value = dbwrap_record_get_value(rec);
504 status = xattr_tdb_pull_attrs(rec, &value, &attribs);
506 if (!NT_STATUS_IS_OK(status)) {
507 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
508 nt_errstr(status)));
509 TALLOC_FREE(rec);
510 return -1;
513 for (i=0; i<attribs->num_eas; i++) {
514 if (strcmp(attribs->eas[i].name, name) == 0) {
515 break;
519 if (i == attribs->num_eas) {
520 TALLOC_FREE(rec);
521 errno = ENOATTR;
522 return -1;
525 attribs->eas[i] =
526 attribs->eas[attribs->num_eas-1];
527 attribs->num_eas -= 1;
529 if (attribs->num_eas == 0) {
530 dbwrap_record_delete(rec);
531 TALLOC_FREE(rec);
532 return 0;
535 status = xattr_tdb_save_attrs(rec, attribs);
537 TALLOC_FREE(rec);
539 if (!NT_STATUS_IS_OK(status)) {
540 DEBUG(1, ("save failed: %s\n", nt_errstr(status)));
541 return -1;
544 return 0;
547 static int xattr_tdb_removexattr(struct vfs_handle_struct *handle,
548 const char *path, const char *name)
550 SMB_STRUCT_STAT sbuf;
551 struct file_id id;
552 struct db_context *db;
554 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
556 if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
557 return -1;
560 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
562 return xattr_tdb_removeattr(db, &id, name);
565 static int xattr_tdb_fremovexattr(struct vfs_handle_struct *handle,
566 struct files_struct *fsp, const char *name)
568 SMB_STRUCT_STAT sbuf;
569 struct file_id id;
570 struct db_context *db;
572 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
574 if (SMB_VFS_FSTAT(fsp, &sbuf) == -1) {
575 return -1;
578 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
580 return xattr_tdb_removeattr(db, &id, name);
584 * Open the tdb file upon VFS_CONNECT
587 static bool xattr_tdb_init(int snum, struct db_context **p_db)
589 struct db_context *db;
590 const char *dbname;
591 char *def_dbname;
593 def_dbname = state_path("xattr.tdb");
594 if (def_dbname == NULL) {
595 errno = ENOSYS;
596 return false;
599 dbname = lp_parm_const_string(snum, "xattr_tdb", "file", def_dbname);
601 /* now we know dbname is not NULL */
603 become_root();
604 db = db_open(NULL, dbname, 0, TDB_DEFAULT, O_RDWR|O_CREAT, 0600,
605 DBWRAP_LOCK_ORDER_2);
606 unbecome_root();
608 if (db == NULL) {
609 #if defined(ENOTSUP)
610 errno = ENOTSUP;
611 #else
612 errno = ENOSYS;
613 #endif
614 TALLOC_FREE(def_dbname);
615 return false;
618 *p_db = db;
619 TALLOC_FREE(def_dbname);
620 return true;
624 * On unlink we need to delete the tdb record
626 static int xattr_tdb_unlink(vfs_handle_struct *handle,
627 const struct smb_filename *smb_fname)
629 struct smb_filename *smb_fname_tmp = NULL;
630 struct file_id id;
631 struct db_context *db;
632 struct db_record *rec;
633 NTSTATUS status;
634 int ret = -1;
635 bool remove_record = false;
637 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
639 status = copy_smb_filename(talloc_tos(), smb_fname, &smb_fname_tmp);
640 if (!NT_STATUS_IS_OK(status)) {
641 errno = map_errno_from_nt_status(status);
642 return -1;
645 if (lp_posix_pathnames()) {
646 ret = SMB_VFS_LSTAT(handle->conn, smb_fname_tmp);
647 } else {
648 ret = SMB_VFS_STAT(handle->conn, smb_fname_tmp);
650 if (ret == -1) {
651 goto out;
654 if (smb_fname_tmp->st.st_ex_nlink == 1) {
655 /* Only remove record on last link to file. */
656 remove_record = true;
659 ret = SMB_VFS_NEXT_UNLINK(handle, smb_fname_tmp);
661 if (ret == -1) {
662 goto out;
665 if (!remove_record) {
666 goto out;
669 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &smb_fname_tmp->st);
671 rec = xattr_tdb_lock_attrs(talloc_tos(), db, &id);
674 * If rec == NULL there's not much we can do about it
677 if (rec != NULL) {
678 dbwrap_record_delete(rec);
679 TALLOC_FREE(rec);
682 out:
683 TALLOC_FREE(smb_fname_tmp);
684 return ret;
688 * On rmdir we need to delete the tdb record
690 static int xattr_tdb_rmdir(vfs_handle_struct *handle, const char *path)
692 SMB_STRUCT_STAT sbuf;
693 struct file_id id;
694 struct db_context *db;
695 struct db_record *rec;
696 int ret;
698 SMB_VFS_HANDLE_GET_DATA(handle, db, struct db_context, return -1);
700 if (vfs_stat_smb_fname(handle->conn, path, &sbuf) == -1) {
701 return -1;
704 ret = SMB_VFS_NEXT_RMDIR(handle, path);
706 if (ret == -1) {
707 return -1;
710 id = SMB_VFS_FILE_ID_CREATE(handle->conn, &sbuf);
712 rec = xattr_tdb_lock_attrs(talloc_tos(), db, &id);
715 * If rec == NULL there's not much we can do about it
718 if (rec != NULL) {
719 dbwrap_record_delete(rec);
720 TALLOC_FREE(rec);
723 return 0;
727 * Destructor for the VFS private data
730 static void close_xattr_db(void **data)
732 struct db_context **p_db = (struct db_context **)data;
733 TALLOC_FREE(*p_db);
736 static int xattr_tdb_connect(vfs_handle_struct *handle, const char *service,
737 const char *user)
739 char *sname = NULL;
740 int res, snum;
741 struct db_context *db;
743 res = SMB_VFS_NEXT_CONNECT(handle, service, user);
744 if (res < 0) {
745 return res;
748 snum = find_service(talloc_tos(), service, &sname);
749 if (snum == -1 || sname == NULL) {
751 * Should not happen, but we should not fail just *here*.
753 return 0;
756 if (!xattr_tdb_init(snum, &db)) {
757 DEBUG(5, ("Could not init xattr tdb\n"));
758 lp_do_parameter(snum, "ea support", "False");
759 return 0;
762 lp_do_parameter(snum, "ea support", "True");
764 SMB_VFS_HANDLE_SET_DATA(handle, db, close_xattr_db,
765 struct db_context, return -1);
767 return 0;
770 static struct vfs_fn_pointers vfs_xattr_tdb_fns = {
771 .getxattr_fn = xattr_tdb_getxattr,
772 .fgetxattr_fn = xattr_tdb_fgetxattr,
773 .setxattr_fn = xattr_tdb_setxattr,
774 .fsetxattr_fn = xattr_tdb_fsetxattr,
775 .listxattr_fn = xattr_tdb_listxattr,
776 .flistxattr_fn = xattr_tdb_flistxattr,
777 .removexattr_fn = xattr_tdb_removexattr,
778 .fremovexattr_fn = xattr_tdb_fremovexattr,
779 .unlink_fn = xattr_tdb_unlink,
780 .rmdir_fn = xattr_tdb_rmdir,
781 .connect_fn = xattr_tdb_connect,
784 NTSTATUS vfs_xattr_tdb_init(void);
785 NTSTATUS vfs_xattr_tdb_init(void)
787 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION, "xattr_tdb",
788 &vfs_xattr_tdb_fns);