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"
29 #define DBGC_CLASS DBGC_VFS
32 * unmarshall tdb_xattrs
35 static NTSTATUS
xattr_tdb_pull_attrs(TALLOC_CTX
*mem_ctx
,
37 struct tdb_xattrs
**presult
)
40 enum ndr_err_code ndr_err
;
41 struct tdb_xattrs
*result
;
43 if (!(result
= TALLOC_ZERO_P(mem_ctx
, struct tdb_xattrs
))) {
44 return NT_STATUS_NO_MEMORY
;
47 if (data
->dsize
== 0) {
52 blob
= data_blob_const(data
->dptr
, data
->dsize
);
54 ndr_err
= ndr_pull_struct_blob(&blob
, result
, result
,
55 (ndr_pull_flags_fn_t
)ndr_pull_tdb_xattrs
);
57 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
58 DEBUG(0, ("ndr_pull_tdb_xattrs failed: %s\n",
59 ndr_errstr(ndr_err
)));
61 return ndr_map_error2ntstatus(ndr_err
);
72 static NTSTATUS
xattr_tdb_push_attrs(TALLOC_CTX
*mem_ctx
,
73 const struct tdb_xattrs
*attribs
,
77 enum ndr_err_code ndr_err
;
79 ndr_err
= ndr_push_struct_blob(&blob
, mem_ctx
, attribs
,
80 (ndr_push_flags_fn_t
)ndr_push_tdb_xattrs
);
82 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err
)) {
83 DEBUG(0, ("ndr_push_tdb_xattrs failed: %s\n",
84 ndr_errstr(ndr_err
)));
85 return ndr_map_error2ntstatus(ndr_err
);
88 *data
= make_tdb_data(blob
.data
, blob
.length
);
93 * Load tdb_xattrs for a file from the tdb
96 static NTSTATUS
xattr_tdb_load_attrs(TALLOC_CTX
*mem_ctx
,
97 struct db_context
*db_ctx
,
98 const struct file_id
*id
,
99 struct tdb_xattrs
**presult
)
105 /* For backwards compatibility only store the dev/inode. */
106 push_file_id_16((char *)id_buf
, id
);
108 if (db_ctx
->fetch(db_ctx
, mem_ctx
,
109 make_tdb_data(id_buf
, sizeof(id_buf
)),
111 return NT_STATUS_INTERNAL_DB_CORRUPTION
;
114 status
= xattr_tdb_pull_attrs(mem_ctx
, &data
, presult
);
115 TALLOC_FREE(data
.dptr
);
120 * fetch_lock the tdb_ea record for a file
123 static struct db_record
*xattr_tdb_lock_attrs(TALLOC_CTX
*mem_ctx
,
124 struct db_context
*db_ctx
,
125 const struct file_id
*id
)
129 /* For backwards compatibility only store the dev/inode. */
130 push_file_id_16((char *)id_buf
, id
);
131 return db_ctx
->fetch_locked(db_ctx
, mem_ctx
,
132 make_tdb_data(id_buf
, sizeof(id_buf
)));
136 * Save tdb_xattrs to a previously fetch_locked record
139 static NTSTATUS
xattr_tdb_save_attrs(struct db_record
*rec
,
140 const struct tdb_xattrs
*attribs
)
142 TDB_DATA data
= tdb_null
;
145 status
= xattr_tdb_push_attrs(talloc_tos(), attribs
, &data
);
147 if (!NT_STATUS_IS_OK(status
)) {
148 DEBUG(0, ("xattr_tdb_push_attrs failed: %s\n",
153 status
= rec
->store(rec
, data
, 0);
155 TALLOC_FREE(data
.dptr
);
161 * Worker routine for getxattr and fgetxattr
164 static ssize_t
xattr_tdb_getattr(struct db_context
*db_ctx
,
165 const struct file_id
*id
,
166 const char *name
, void *value
, size_t size
)
168 struct tdb_xattrs
*attribs
;
173 DEBUG(10, ("xattr_tdb_getattr called for file %s, name %s\n",
174 file_id_string_tos(id
), name
));
176 status
= xattr_tdb_load_attrs(talloc_tos(), db_ctx
, id
, &attribs
);
178 if (!NT_STATUS_IS_OK(status
)) {
179 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
185 for (i
=0; i
<attribs
->num_eas
; i
++) {
186 if (strcmp(attribs
->eas
[i
].name
, name
) == 0) {
191 if (i
== attribs
->num_eas
) {
196 if (attribs
->eas
[i
].value
.length
> size
) {
201 memcpy(value
, attribs
->eas
[i
].value
.data
,
202 attribs
->eas
[i
].value
.length
);
203 result
= attribs
->eas
[i
].value
.length
;
206 TALLOC_FREE(attribs
);
210 static ssize_t
xattr_tdb_getxattr(struct vfs_handle_struct
*handle
,
211 const char *path
, const char *name
,
212 void *value
, size_t size
)
214 SMB_STRUCT_STAT sbuf
;
216 struct db_context
*db
;
218 SMB_VFS_HANDLE_GET_DATA(handle
, db
, struct db_context
, return -1);
220 if (vfs_stat_smb_fname(handle
->conn
, path
, &sbuf
) == -1) {
224 id
= SMB_VFS_FILE_ID_CREATE(handle
->conn
, &sbuf
);
226 return xattr_tdb_getattr(db
, &id
, name
, value
, size
);
229 static ssize_t
xattr_tdb_fgetxattr(struct vfs_handle_struct
*handle
,
230 struct files_struct
*fsp
,
231 const char *name
, void *value
, size_t size
)
233 SMB_STRUCT_STAT sbuf
;
235 struct db_context
*db
;
237 SMB_VFS_HANDLE_GET_DATA(handle
, db
, struct db_context
, return -1);
239 if (SMB_VFS_FSTAT(fsp
, &sbuf
) == -1) {
243 id
= SMB_VFS_FILE_ID_CREATE(handle
->conn
, &sbuf
);
245 return xattr_tdb_getattr(db
, &id
, name
, value
, size
);
249 * Worker routine for setxattr and fsetxattr
252 static int xattr_tdb_setattr(struct db_context
*db_ctx
,
253 const struct file_id
*id
, const char *name
,
254 const void *value
, size_t size
, int flags
)
257 struct db_record
*rec
;
258 struct tdb_xattrs
*attribs
;
261 DEBUG(10, ("xattr_tdb_setattr called for file %s, name %s\n",
262 file_id_string_tos(id
), name
));
264 rec
= xattr_tdb_lock_attrs(talloc_tos(), db_ctx
, id
);
267 DEBUG(0, ("xattr_tdb_lock_attrs failed\n"));
272 status
= xattr_tdb_pull_attrs(rec
, &rec
->value
, &attribs
);
274 if (!NT_STATUS_IS_OK(status
)) {
275 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
281 for (i
=0; i
<attribs
->num_eas
; i
++) {
282 if (strcmp(attribs
->eas
[i
].name
, name
) == 0) {
283 if (flags
& XATTR_CREATE
) {
292 if (i
== attribs
->num_eas
) {
293 struct xattr_EA
*tmp
;
295 if (flags
& XATTR_REPLACE
) {
301 tmp
= TALLOC_REALLOC_ARRAY(
302 attribs
, attribs
->eas
, struct xattr_EA
,
303 attribs
->num_eas
+ 1);
306 DEBUG(0, ("TALLOC_REALLOC_ARRAY failed\n"));
313 attribs
->num_eas
+= 1;
316 attribs
->eas
[i
].name
= name
;
317 attribs
->eas
[i
].value
.data
= CONST_DISCARD(uint8
*, value
);
318 attribs
->eas
[i
].value
.length
= size
;
320 status
= xattr_tdb_save_attrs(rec
, attribs
);
324 if (!NT_STATUS_IS_OK(status
)) {
325 DEBUG(1, ("save failed: %s\n", nt_errstr(status
)));
332 static int xattr_tdb_setxattr(struct vfs_handle_struct
*handle
,
333 const char *path
, const char *name
,
334 const void *value
, size_t size
, int flags
)
336 SMB_STRUCT_STAT sbuf
;
338 struct db_context
*db
;
340 SMB_VFS_HANDLE_GET_DATA(handle
, db
, struct db_context
, return -1);
342 if (vfs_stat_smb_fname(handle
->conn
, path
, &sbuf
) == -1) {
346 id
= SMB_VFS_FILE_ID_CREATE(handle
->conn
, &sbuf
);
348 return xattr_tdb_setattr(db
, &id
, name
, value
, size
, flags
);
351 static int xattr_tdb_fsetxattr(struct vfs_handle_struct
*handle
,
352 struct files_struct
*fsp
,
353 const char *name
, const void *value
,
354 size_t size
, int flags
)
356 SMB_STRUCT_STAT sbuf
;
358 struct db_context
*db
;
360 SMB_VFS_HANDLE_GET_DATA(handle
, db
, struct db_context
, return -1);
362 if (SMB_VFS_FSTAT(fsp
, &sbuf
) == -1) {
366 id
= SMB_VFS_FILE_ID_CREATE(handle
->conn
, &sbuf
);
368 return xattr_tdb_setattr(db
, &id
, name
, value
, size
, flags
);
372 * Worker routine for listxattr and flistxattr
375 static ssize_t
xattr_tdb_listattr(struct db_context
*db_ctx
,
376 const struct file_id
*id
, char *list
,
380 struct tdb_xattrs
*attribs
;
384 status
= xattr_tdb_load_attrs(talloc_tos(), db_ctx
, id
, &attribs
);
386 if (!NT_STATUS_IS_OK(status
)) {
387 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
393 DEBUG(10, ("xattr_tdb_listattr: Found %d xattrs\n",
396 for (i
=0; i
<attribs
->num_eas
; i
++) {
399 DEBUG(10, ("xattr_tdb_listattr: xattrs[i].name: %s\n",
400 attribs
->eas
[i
].name
));
402 tmp
= strlen(attribs
->eas
[i
].name
);
405 * Try to protect against overflow
408 if (len
+ (tmp
+1) < len
) {
409 TALLOC_FREE(attribs
);
415 * Take care of the terminating NULL
421 TALLOC_FREE(attribs
);
428 for (i
=0; i
<attribs
->num_eas
; i
++) {
429 strlcpy(list
+len
, attribs
->eas
[i
].name
,
431 len
+= (strlen(attribs
->eas
[i
].name
) + 1);
434 TALLOC_FREE(attribs
);
438 static ssize_t
xattr_tdb_listxattr(struct vfs_handle_struct
*handle
,
439 const char *path
, char *list
, size_t size
)
441 SMB_STRUCT_STAT sbuf
;
443 struct db_context
*db
;
445 SMB_VFS_HANDLE_GET_DATA(handle
, db
, struct db_context
, return -1);
447 if (vfs_stat_smb_fname(handle
->conn
, path
, &sbuf
) == -1) {
451 id
= SMB_VFS_FILE_ID_CREATE(handle
->conn
, &sbuf
);
453 return xattr_tdb_listattr(db
, &id
, list
, size
);
456 static ssize_t
xattr_tdb_flistxattr(struct vfs_handle_struct
*handle
,
457 struct files_struct
*fsp
, char *list
,
460 SMB_STRUCT_STAT sbuf
;
462 struct db_context
*db
;
464 SMB_VFS_HANDLE_GET_DATA(handle
, db
, struct db_context
, return -1);
466 if (SMB_VFS_FSTAT(fsp
, &sbuf
) == -1) {
470 id
= SMB_VFS_FILE_ID_CREATE(handle
->conn
, &sbuf
);
472 return xattr_tdb_listattr(db
, &id
, list
, size
);
476 * Worker routine for removexattr and fremovexattr
479 static int xattr_tdb_removeattr(struct db_context
*db_ctx
,
480 const struct file_id
*id
, const char *name
)
483 struct db_record
*rec
;
484 struct tdb_xattrs
*attribs
;
487 rec
= xattr_tdb_lock_attrs(talloc_tos(), db_ctx
, id
);
490 DEBUG(0, ("xattr_tdb_lock_attrs failed\n"));
495 status
= xattr_tdb_pull_attrs(rec
, &rec
->value
, &attribs
);
497 if (!NT_STATUS_IS_OK(status
)) {
498 DEBUG(10, ("xattr_tdb_fetch_attrs failed: %s\n",
504 for (i
=0; i
<attribs
->num_eas
; i
++) {
505 if (strcmp(attribs
->eas
[i
].name
, name
) == 0) {
510 if (i
== attribs
->num_eas
) {
517 attribs
->eas
[attribs
->num_eas
-1];
518 attribs
->num_eas
-= 1;
520 if (attribs
->num_eas
== 0) {
521 rec
->delete_rec(rec
);
526 status
= xattr_tdb_save_attrs(rec
, attribs
);
530 if (!NT_STATUS_IS_OK(status
)) {
531 DEBUG(1, ("save failed: %s\n", nt_errstr(status
)));
538 static int xattr_tdb_removexattr(struct vfs_handle_struct
*handle
,
539 const char *path
, const char *name
)
541 SMB_STRUCT_STAT sbuf
;
543 struct db_context
*db
;
545 SMB_VFS_HANDLE_GET_DATA(handle
, db
, struct db_context
, return -1);
547 if (vfs_stat_smb_fname(handle
->conn
, path
, &sbuf
) == -1) {
551 id
= SMB_VFS_FILE_ID_CREATE(handle
->conn
, &sbuf
);
553 return xattr_tdb_removeattr(db
, &id
, name
);
556 static int xattr_tdb_fremovexattr(struct vfs_handle_struct
*handle
,
557 struct files_struct
*fsp
, const char *name
)
559 SMB_STRUCT_STAT sbuf
;
561 struct db_context
*db
;
563 SMB_VFS_HANDLE_GET_DATA(handle
, db
, struct db_context
, return -1);
565 if (SMB_VFS_FSTAT(fsp
, &sbuf
) == -1) {
569 id
= SMB_VFS_FILE_ID_CREATE(handle
->conn
, &sbuf
);
571 return xattr_tdb_removeattr(db
, &id
, name
);
575 * Open the tdb file upon VFS_CONNECT
578 static bool xattr_tdb_init(int snum
, struct db_context
**p_db
)
580 struct db_context
*db
;
584 def_dbname
= state_path("xattr.tdb");
585 if (def_dbname
== NULL
) {
590 dbname
= lp_parm_const_string(snum
, "xattr_tdb", "file", def_dbname
);
592 /* now we know dbname is not NULL */
595 db
= db_open(NULL
, dbname
, 0, TDB_DEFAULT
, O_RDWR
|O_CREAT
, 0600);
604 TALLOC_FREE(def_dbname
);
609 TALLOC_FREE(def_dbname
);
614 * On unlink we need to delete the tdb record
616 static int xattr_tdb_unlink(vfs_handle_struct
*handle
,
617 const struct smb_filename
*smb_fname
)
619 struct smb_filename
*smb_fname_tmp
= NULL
;
621 struct db_context
*db
;
622 struct db_record
*rec
;
625 bool remove_record
= false;
627 SMB_VFS_HANDLE_GET_DATA(handle
, db
, struct db_context
, return -1);
629 status
= copy_smb_filename(talloc_tos(), smb_fname
, &smb_fname_tmp
);
630 if (!NT_STATUS_IS_OK(status
)) {
631 errno
= map_errno_from_nt_status(status
);
635 if (lp_posix_pathnames()) {
636 ret
= SMB_VFS_LSTAT(handle
->conn
, smb_fname_tmp
);
638 ret
= SMB_VFS_STAT(handle
->conn
, smb_fname_tmp
);
644 if (smb_fname_tmp
->st
.st_ex_nlink
== 1) {
645 /* Only remove record on last link to file. */
646 remove_record
= true;
649 ret
= SMB_VFS_NEXT_UNLINK(handle
, smb_fname_tmp
);
655 if (!remove_record
) {
659 id
= SMB_VFS_FILE_ID_CREATE(handle
->conn
, &smb_fname_tmp
->st
);
661 rec
= xattr_tdb_lock_attrs(talloc_tos(), db
, &id
);
664 * If rec == NULL there's not much we can do about it
668 rec
->delete_rec(rec
);
673 TALLOC_FREE(smb_fname_tmp
);
678 * On rmdir we need to delete the tdb record
680 static int xattr_tdb_rmdir(vfs_handle_struct
*handle
, const char *path
)
682 SMB_STRUCT_STAT sbuf
;
684 struct db_context
*db
;
685 struct db_record
*rec
;
688 SMB_VFS_HANDLE_GET_DATA(handle
, db
, struct db_context
, return -1);
690 if (vfs_stat_smb_fname(handle
->conn
, path
, &sbuf
) == -1) {
694 ret
= SMB_VFS_NEXT_RMDIR(handle
, path
);
700 id
= SMB_VFS_FILE_ID_CREATE(handle
->conn
, &sbuf
);
702 rec
= xattr_tdb_lock_attrs(talloc_tos(), db
, &id
);
705 * If rec == NULL there's not much we can do about it
709 rec
->delete_rec(rec
);
717 * Destructor for the VFS private data
720 static void close_xattr_db(void **data
)
722 struct db_context
**p_db
= (struct db_context
**)data
;
726 static int xattr_tdb_connect(vfs_handle_struct
*handle
, const char *service
,
731 struct db_context
*db
;
733 res
= SMB_VFS_NEXT_CONNECT(handle
, service
, user
);
738 snum
= find_service(talloc_tos(), service
, &sname
);
739 if (snum
== -1 || sname
== NULL
) {
741 * Should not happen, but we should not fail just *here*.
746 if (!xattr_tdb_init(snum
, &db
)) {
747 DEBUG(5, ("Could not init xattr tdb\n"));
748 lp_do_parameter(snum
, "ea support", "False");
752 lp_do_parameter(snum
, "ea support", "True");
754 SMB_VFS_HANDLE_SET_DATA(handle
, db
, close_xattr_db
,
755 struct db_context
, return -1);
760 static struct vfs_fn_pointers vfs_xattr_tdb_fns
= {
761 .getxattr
= xattr_tdb_getxattr
,
762 .fgetxattr
= xattr_tdb_fgetxattr
,
763 .setxattr
= xattr_tdb_setxattr
,
764 .fsetxattr
= xattr_tdb_fsetxattr
,
765 .listxattr
= xattr_tdb_listxattr
,
766 .flistxattr
= xattr_tdb_flistxattr
,
767 .removexattr
= xattr_tdb_removexattr
,
768 .fremovexattr
= xattr_tdb_fremovexattr
,
769 .unlink
= xattr_tdb_unlink
,
770 .rmdir
= xattr_tdb_rmdir
,
771 .connect_fn
= xattr_tdb_connect
,
774 NTSTATUS
vfs_xattr_tdb_init(void);
775 NTSTATUS
vfs_xattr_tdb_init(void)
777 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
, "xattr_tdb",