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",