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 "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"
31 #define DBGC_CLASS DBGC_VFS
34 * unmarshall tdb_xattrs
37 static NTSTATUS
xattr_tdb_pull_attrs(TALLOC_CTX
*mem_ctx
,
39 struct tdb_xattrs
**presult
)
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) {
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
)));
63 return ndr_map_error2ntstatus(ndr_err
);
74 static NTSTATUS
xattr_tdb_push_attrs(TALLOC_CTX
*mem_ctx
,
75 const struct tdb_xattrs
*attribs
,
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
);
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
)
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
)),
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
);
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
)
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
;
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",
156 status
= dbwrap_record_store(rec
, data
, 0);
158 TALLOC_FREE(data
.dptr
);
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
;
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",
188 for (i
=0; i
<attribs
->num_eas
; i
++) {
189 if (strcmp(attribs
->eas
[i
].name
, name
) == 0) {
194 if (i
== attribs
->num_eas
) {
199 if (attribs
->eas
[i
].value
.length
> size
) {
204 memcpy(value
, attribs
->eas
[i
].value
.data
,
205 attribs
->eas
[i
].value
.length
);
206 result
= attribs
->eas
[i
].value
.length
;
209 TALLOC_FREE(attribs
);
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
;
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) {
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
;
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) {
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
)
260 struct db_record
*rec
;
261 struct tdb_xattrs
*attribs
;
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
);
271 DEBUG(0, ("xattr_tdb_lock_attrs failed\n"));
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",
287 for (i
=0; i
<attribs
->num_eas
; i
++) {
288 if (strcmp(attribs
->eas
[i
].name
, name
) == 0) {
289 if (flags
& XATTR_CREATE
) {
298 if (i
== attribs
->num_eas
) {
299 struct xattr_EA
*tmp
;
301 if (flags
& XATTR_REPLACE
) {
307 tmp
= talloc_realloc(
308 attribs
, attribs
->eas
, struct xattr_EA
,
309 attribs
->num_eas
+ 1);
312 DEBUG(0, ("talloc_realloc failed\n"));
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
);
330 if (!NT_STATUS_IS_OK(status
)) {
331 DEBUG(1, ("save failed: %s\n", nt_errstr(status
)));
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
;
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) {
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
;
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) {
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
,
386 struct tdb_xattrs
*attribs
;
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",
399 DEBUG(10, ("xattr_tdb_listattr: Found %d xattrs\n",
402 for (i
=0; i
<attribs
->num_eas
; i
++) {
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
);
421 * Take care of the terminating NULL
427 TALLOC_FREE(attribs
);
434 for (i
=0; i
<attribs
->num_eas
; i
++) {
435 strlcpy(list
+len
, attribs
->eas
[i
].name
,
437 len
+= (strlen(attribs
->eas
[i
].name
) + 1);
440 TALLOC_FREE(attribs
);
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
;
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) {
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
,
466 SMB_STRUCT_STAT sbuf
;
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) {
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
)
489 struct db_record
*rec
;
490 struct tdb_xattrs
*attribs
;
494 rec
= xattr_tdb_lock_attrs(talloc_tos(), db_ctx
, id
);
497 DEBUG(0, ("xattr_tdb_lock_attrs failed\n"));
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",
513 for (i
=0; i
<attribs
->num_eas
; i
++) {
514 if (strcmp(attribs
->eas
[i
].name
, name
) == 0) {
519 if (i
== attribs
->num_eas
) {
526 attribs
->eas
[attribs
->num_eas
-1];
527 attribs
->num_eas
-= 1;
529 if (attribs
->num_eas
== 0) {
530 dbwrap_record_delete(rec
);
535 status
= xattr_tdb_save_attrs(rec
, attribs
);
539 if (!NT_STATUS_IS_OK(status
)) {
540 DEBUG(1, ("save failed: %s\n", nt_errstr(status
)));
547 static int xattr_tdb_removexattr(struct vfs_handle_struct
*handle
,
548 const char *path
, const char *name
)
550 SMB_STRUCT_STAT sbuf
;
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) {
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
;
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) {
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
;
593 def_dbname
= state_path("xattr.tdb");
594 if (def_dbname
== NULL
) {
599 dbname
= lp_parm_const_string(snum
, "xattr_tdb", "file", def_dbname
);
601 /* now we know dbname is not NULL */
604 db
= db_open(NULL
, dbname
, 0, TDB_DEFAULT
, O_RDWR
|O_CREAT
, 0600,
605 DBWRAP_LOCK_ORDER_2
);
614 TALLOC_FREE(def_dbname
);
619 TALLOC_FREE(def_dbname
);
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
;
631 struct db_context
*db
;
632 struct db_record
*rec
;
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
);
645 if (lp_posix_pathnames()) {
646 ret
= SMB_VFS_LSTAT(handle
->conn
, smb_fname_tmp
);
648 ret
= SMB_VFS_STAT(handle
->conn
, smb_fname_tmp
);
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
);
665 if (!remove_record
) {
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
678 dbwrap_record_delete(rec
);
683 TALLOC_FREE(smb_fname_tmp
);
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
;
694 struct db_context
*db
;
695 struct db_record
*rec
;
698 SMB_VFS_HANDLE_GET_DATA(handle
, db
, struct db_context
, return -1);
700 if (vfs_stat_smb_fname(handle
->conn
, path
, &sbuf
) == -1) {
704 ret
= SMB_VFS_NEXT_RMDIR(handle
, path
);
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
719 dbwrap_record_delete(rec
);
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
;
736 static int xattr_tdb_connect(vfs_handle_struct
*handle
, const char *service
,
741 struct db_context
*db
;
743 res
= SMB_VFS_NEXT_CONNECT(handle
, service
, user
);
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*.
756 if (!xattr_tdb_init(snum
, &db
)) {
757 DEBUG(5, ("Could not init xattr tdb\n"));
758 lp_do_parameter(snum
, "ea support", "False");
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);
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",