2 * Store posix-level xattrs in a tdb
4 * Copyright (C) Volker Lendecke, 2007
5 * Copyright (C) Andrew Bartlett, 2012
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, see <http://www.gnu.org/licenses/>.
22 #include "system/filesys.h"
23 #include "smbd/smbd.h"
24 #include "dbwrap/dbwrap.h"
25 #include "dbwrap/dbwrap_open.h"
26 #include "source3/lib/xattr_tdb.h"
27 #include "lib/util/tevent_unix.h"
30 #define DBGC_CLASS DBGC_VFS
32 struct xattr_tdb_config
{
33 struct db_context
*db
;
34 bool ignore_user_xattr
;
37 static bool xattr_tdb_init(struct vfs_handle_struct
*handle
,
38 struct xattr_tdb_config
**_config
);
40 static bool is_user_xattr(const char *xattr_name
)
44 match
= strncmp(xattr_name
, "user.", strlen("user."));
48 static int xattr_tdb_get_file_id(struct vfs_handle_struct
*handle
,
49 const char *path
, struct file_id
*id
)
52 TALLOC_CTX
*frame
= talloc_stackframe();
53 struct smb_filename
*smb_fname
;
55 smb_fname
= synthetic_smb_fname(frame
,
61 if (smb_fname
== NULL
) {
67 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
74 *id
= SMB_VFS_NEXT_FILE_ID_CREATE(handle
, &smb_fname
->st
);
79 struct xattr_tdb_getxattrat_state
{
80 struct vfs_aio_state vfs_aio_state
;
85 static void xattr_tdb_getxattrat_done(struct tevent_req
*subreq
);
87 static struct tevent_req
*xattr_tdb_getxattrat_send(
89 struct tevent_context
*ev
,
90 struct vfs_handle_struct
*handle
,
91 files_struct
*dir_fsp
,
92 const struct smb_filename
*smb_fname
,
93 const char *xattr_name
,
96 struct xattr_tdb_config
*config
= NULL
;
97 struct tevent_req
*req
= NULL
;
98 struct tevent_req
*subreq
= NULL
;
99 struct xattr_tdb_getxattrat_state
*state
= NULL
;
100 struct smb_filename
*cwd
= NULL
;
105 DATA_BLOB xattr_blob
;
107 if (!xattr_tdb_init(handle
, &config
)) {
111 req
= tevent_req_create(mem_ctx
, &state
,
112 struct xattr_tdb_getxattrat_state
);
116 state
->xattr_size
= -1;
118 if (config
->ignore_user_xattr
&& is_user_xattr(xattr_name
)) {
119 subreq
= SMB_VFS_NEXT_GETXATTRAT_SEND(state
,
126 if (tevent_req_nomem(subreq
, req
)) {
127 return tevent_req_post(req
, ev
);
129 tevent_req_set_callback(subreq
, xattr_tdb_getxattrat_done
, req
);
133 cwd
= SMB_VFS_GETWD(dir_fsp
->conn
, state
);
134 if (tevent_req_nomem(cwd
, req
)) {
135 return tevent_req_post(req
, ev
);
138 ret
= SMB_VFS_CHDIR(dir_fsp
->conn
, dir_fsp
->fsp_name
);
140 tevent_req_error(req
, errno
);
141 return tevent_req_post(req
, ev
);
144 ret
= xattr_tdb_get_file_id(handle
, smb_fname
->base_name
, &id
);
147 cwd_ret
= SMB_VFS_CHDIR(dir_fsp
->conn
, cwd
);
148 SMB_ASSERT(cwd_ret
== 0);
151 tevent_req_error(req
, error
);
152 return tevent_req_post(req
, ev
);
155 state
->xattr_size
= xattr_tdb_getattr(config
->db
,
160 if (state
->xattr_size
== -1) {
161 tevent_req_error(req
, errno
);
162 return tevent_req_post(req
, ev
);
165 if (alloc_hint
== 0) {
167 * The caller only wants to know the size.
169 tevent_req_done(req
);
170 return tevent_req_post(req
, ev
);
173 if (state
->xattr_size
== 0) {
177 tevent_req_done(req
);
178 return tevent_req_post(req
, ev
);
181 if (xattr_blob
.length
> alloc_hint
) {
183 * The data doesn't fit.
185 state
->xattr_size
= -1;
186 tevent_req_error(req
, ERANGE
);
187 return tevent_req_post(req
, ev
);
191 * take the whole blob.
193 state
->xattr_value
= xattr_blob
.data
;
195 tevent_req_done(req
);
196 return tevent_req_post(req
, ev
);
199 static void xattr_tdb_getxattrat_done(struct tevent_req
*subreq
)
201 struct tevent_req
*req
= tevent_req_callback_data(
202 subreq
, struct tevent_req
);
203 struct xattr_tdb_getxattrat_state
*state
= tevent_req_data(
204 req
, struct xattr_tdb_getxattrat_state
);
206 state
->xattr_size
= SMB_VFS_NEXT_GETXATTRAT_RECV(subreq
,
207 &state
->vfs_aio_state
,
209 &state
->xattr_value
);
211 if (state
->xattr_size
== -1) {
212 tevent_req_error(req
, state
->vfs_aio_state
.error
);
216 tevent_req_done(req
);
220 static ssize_t
xattr_tdb_getxattrat_recv(struct tevent_req
*req
,
221 struct vfs_aio_state
*aio_state
,
223 uint8_t **xattr_value
)
225 struct xattr_tdb_getxattrat_state
*state
= tevent_req_data(
226 req
, struct xattr_tdb_getxattrat_state
);
229 if (tevent_req_is_unix_error(req
, &aio_state
->error
)) {
230 tevent_req_received(req
);
234 *aio_state
= state
->vfs_aio_state
;
235 xattr_size
= state
->xattr_size
;
236 if (xattr_value
!= NULL
) {
237 *xattr_value
= talloc_move(mem_ctx
, &state
->xattr_value
);
240 tevent_req_received(req
);
244 static ssize_t
xattr_tdb_fgetxattr(struct vfs_handle_struct
*handle
,
245 struct files_struct
*fsp
,
246 const char *name
, void *value
, size_t size
)
248 struct xattr_tdb_config
*config
= NULL
;
249 SMB_STRUCT_STAT sbuf
;
253 TALLOC_CTX
*frame
= NULL
;
255 if (!xattr_tdb_init(handle
, &config
)) {
259 if (config
->ignore_user_xattr
&& is_user_xattr(name
)) {
260 return SMB_VFS_NEXT_FGETXATTR(
261 handle
, fsp
, name
, value
, size
);
264 if (SMB_VFS_NEXT_FSTAT(handle
, fsp
, &sbuf
) == -1) {
268 frame
= talloc_stackframe();
270 id
= SMB_VFS_NEXT_FILE_ID_CREATE(handle
, &sbuf
);
272 xattr_size
= xattr_tdb_getattr(config
->db
, frame
, &id
, name
, &blob
);
273 if (xattr_size
< 0) {
284 if (blob
.length
> size
) {
289 memcpy(value
, blob
.data
, xattr_size
);
294 static int xattr_tdb_fsetxattr(struct vfs_handle_struct
*handle
,
295 struct files_struct
*fsp
,
296 const char *name
, const void *value
,
297 size_t size
, int flags
)
299 struct xattr_tdb_config
*config
= NULL
;
300 SMB_STRUCT_STAT sbuf
;
304 if (!xattr_tdb_init(handle
, &config
)) {
308 if (config
->ignore_user_xattr
&& is_user_xattr(name
)) {
309 return SMB_VFS_NEXT_FSETXATTR(
310 handle
, fsp
, name
, value
, size
, flags
);
313 if (SMB_VFS_NEXT_FSTAT(handle
, fsp
, &sbuf
) == -1) {
317 id
= SMB_VFS_NEXT_FILE_ID_CREATE(handle
, &sbuf
);
319 ret
= xattr_tdb_setattr(config
->db
, &id
, name
, value
, size
, flags
);
324 static ssize_t
xattr_tdb_flistxattr(struct vfs_handle_struct
*handle
,
325 struct files_struct
*fsp
, char *list
,
328 struct xattr_tdb_config
*config
= NULL
;
329 SMB_STRUCT_STAT sbuf
;
331 ssize_t backend_size
;
334 if (!xattr_tdb_init(handle
, &config
)) {
338 if (SMB_VFS_NEXT_FSTAT(handle
, fsp
, &sbuf
) == -1) {
342 id
= SMB_VFS_NEXT_FILE_ID_CREATE(handle
, &sbuf
);
344 ret
= xattr_tdb_listattr(config
->db
, &id
, list
, size
);
351 if (!config
->ignore_user_xattr
) {
354 SMB_ASSERT(ret
< size
);
356 backend_size
= SMB_VFS_NEXT_FLISTXATTR(
357 handle
, fsp
, list
+ ret
, size
- ret
);
358 if (backend_size
== -1) {
362 return ret
+ backend_size
;
365 static int xattr_tdb_fremovexattr(struct vfs_handle_struct
*handle
,
366 struct files_struct
*fsp
, const char *name
)
368 struct xattr_tdb_config
*config
= NULL
;
369 SMB_STRUCT_STAT sbuf
;
372 if (!xattr_tdb_init(handle
, &config
)) {
376 if (config
->ignore_user_xattr
&& is_user_xattr(name
)) {
377 return SMB_VFS_NEXT_FREMOVEXATTR(handle
, fsp
, name
);
380 if (SMB_VFS_NEXT_FSTAT(handle
, fsp
, &sbuf
) == -1) {
384 id
= SMB_VFS_NEXT_FILE_ID_CREATE(handle
, &sbuf
);
386 return xattr_tdb_removeattr(config
->db
, &id
, name
);
390 * Destructor for the VFS private data
393 static void config_destructor(void **data
)
395 struct xattr_tdb_config
**config
= (struct xattr_tdb_config
**)data
;
396 TALLOC_FREE((*config
)->db
);
400 * Open the tdb file upon VFS_CONNECT
403 static bool xattr_tdb_init(struct vfs_handle_struct
*handle
,
404 struct xattr_tdb_config
**_config
)
406 struct xattr_tdb_config
*config
= NULL
;
410 if (SMB_VFS_HANDLE_TEST_DATA(handle
)) {
411 SMB_VFS_HANDLE_GET_DATA(handle
, config
, struct xattr_tdb_config
,
413 if (_config
!= NULL
) {
419 config
= talloc_zero(handle
->conn
, struct xattr_tdb_config
);
420 if (config
== NULL
) {
425 def_dbname
= state_path(talloc_tos(), "xattr.tdb");
426 if (def_dbname
== NULL
) {
431 dbname
= lp_parm_const_string(SNUM(handle
->conn
),
436 /* now we know dbname is not NULL */
439 config
->db
= db_open(handle
, dbname
, 0, TDB_DEFAULT
, O_RDWR
|O_CREAT
, 0600,
440 DBWRAP_LOCK_ORDER_2
, DBWRAP_FLAG_NONE
);
443 if (config
->db
== NULL
) {
449 TALLOC_FREE(def_dbname
);
452 TALLOC_FREE(def_dbname
);
454 config
->ignore_user_xattr
= lp_parm_bool(
455 SNUM(handle
->conn
), "xattr_tdb", "ignore_user_xattr", false);
457 SMB_VFS_HANDLE_SET_DATA(handle
, config
, config_destructor
,
458 struct xattr_tdb_config
, return false);
460 if (_config
!= NULL
) {
466 DBG_WARNING("Failed to initialize config: %s\n", strerror(errno
));
467 lp_do_parameter(SNUM(handle
->conn
), "ea support", "False");
471 static int xattr_tdb_openat(struct vfs_handle_struct
*handle
,
472 const struct files_struct
*dirfsp
,
473 const struct smb_filename
*smb_fname
,
474 struct files_struct
*fsp
,
475 const struct vfs_open_how
*how
)
477 struct xattr_tdb_config
*config
= NULL
;
478 SMB_STRUCT_STAT sbuf
;
482 if (!xattr_tdb_init(handle
, &config
)) {
486 fd
= SMB_VFS_NEXT_OPENAT(handle
,
495 if ((how
->flags
& (O_CREAT
|O_EXCL
)) != (O_CREAT
|O_EXCL
)) {
500 * We know we used O_CREAT|O_EXCL and it worked.
501 * We must have created the file.
505 ret
= SMB_VFS_FSTAT(fsp
, &sbuf
);
508 /* Can't happen... */
509 DBG_WARNING("SMB_VFS_FSTAT failed on file %s (%s)\n",
510 smb_fname_str_dbg(smb_fname
),
515 fsp
->file_id
= SMB_VFS_FILE_ID_CREATE(fsp
->conn
, &sbuf
);
517 xattr_tdb_remove_all_attrs(config
->db
, &fsp
->file_id
);
522 static int xattr_tdb_mkdirat(vfs_handle_struct
*handle
,
523 struct files_struct
*dirfsp
,
524 const struct smb_filename
*smb_fname
,
527 struct xattr_tdb_config
*config
= NULL
;
528 struct file_id fileid
;
529 struct stat_ex sbuf
= { .st_ex_nlink
= 0, };
532 if (!xattr_tdb_init(handle
, &config
)) {
536 ret
= SMB_VFS_NEXT_MKDIRAT(handle
,
544 ret
= SMB_VFS_NEXT_FSTATAT(
545 handle
, dirfsp
, smb_fname
, &sbuf
, AT_SYMLINK_NOFOLLOW
);
548 /* Rename race. Let upper level take care of it. */
551 if (!S_ISDIR(sbuf
.st_ex_mode
)) {
552 /* Rename race. Let upper level take care of it. */
556 fileid
= SMB_VFS_FILE_ID_CREATE(handle
->conn
, &sbuf
);
558 xattr_tdb_remove_all_attrs(config
->db
, &fileid
);
563 * On unlink we need to delete the tdb record
565 static int xattr_tdb_unlinkat(vfs_handle_struct
*handle
,
566 struct files_struct
*dirfsp
,
567 const struct smb_filename
*smb_fname
,
570 struct xattr_tdb_config
*config
= NULL
;
571 struct smb_filename
*smb_fname_tmp
= NULL
;
572 struct smb_filename
*full_fname
= NULL
;
575 bool remove_record
= false;
576 TALLOC_CTX
*frame
= NULL
;
578 if (!xattr_tdb_init(handle
, &config
)) {
582 frame
= talloc_stackframe();
584 smb_fname_tmp
= cp_smb_filename(frame
, smb_fname
);
585 if (smb_fname_tmp
== NULL
) {
592 * TODO: use SMB_VFS_STATX() once we have that
595 full_fname
= full_path_from_dirfsp_atname(frame
,
598 if (full_fname
== NULL
) {
602 if (full_fname
->flags
& SMB_FILENAME_POSIX_PATH
) {
603 ret
= SMB_VFS_NEXT_LSTAT(handle
, full_fname
);
605 ret
= SMB_VFS_NEXT_STAT(handle
, full_fname
);
606 if (ret
== -1 && (errno
== ENOENT
|| errno
== ELOOP
)) {
607 if (VALID_STAT(smb_fname
->st
) &&
608 S_ISLNK(smb_fname
->st
.st_ex_mode
)) {
610 * Original name was a link - Could be
611 * trying to remove a dangling symlink.
613 ret
= SMB_VFS_NEXT_LSTAT(handle
, full_fname
);
620 smb_fname_tmp
->st
= full_fname
->st
;
622 if (flags
& AT_REMOVEDIR
) {
623 /* Always remove record when removing a directory succeeds. */
624 remove_record
= true;
626 if (smb_fname_tmp
->st
.st_ex_nlink
== 1) {
627 /* Only remove record on last link to file. */
628 remove_record
= true;
632 ret
= SMB_VFS_NEXT_UNLINKAT(handle
,
641 if (!remove_record
) {
645 id
= SMB_VFS_NEXT_FILE_ID_CREATE(handle
, &smb_fname_tmp
->st
);
647 xattr_tdb_remove_all_attrs(config
->db
, &id
);
654 static int xattr_tdb_connect(vfs_handle_struct
*handle
, const char *service
,
660 res
= SMB_VFS_NEXT_CONNECT(handle
, service
, user
);
665 snum
= find_service(talloc_tos(), service
, &sname
);
666 if (snum
== -1 || sname
== NULL
) {
668 * Should not happen, but we should not fail just *here*.
673 if (!xattr_tdb_init(handle
, NULL
)) {
674 DEBUG(5, ("Could not init xattr tdb\n"));
675 lp_do_parameter(snum
, "ea support", "False");
679 lp_do_parameter(snum
, "ea support", "True");
684 static struct vfs_fn_pointers vfs_xattr_tdb_fns
= {
685 .getxattrat_send_fn
= xattr_tdb_getxattrat_send
,
686 .getxattrat_recv_fn
= xattr_tdb_getxattrat_recv
,
687 .fgetxattr_fn
= xattr_tdb_fgetxattr
,
688 .fsetxattr_fn
= xattr_tdb_fsetxattr
,
689 .flistxattr_fn
= xattr_tdb_flistxattr
,
690 .fremovexattr_fn
= xattr_tdb_fremovexattr
,
691 .openat_fn
= xattr_tdb_openat
,
692 .mkdirat_fn
= xattr_tdb_mkdirat
,
693 .unlinkat_fn
= xattr_tdb_unlinkat
,
694 .connect_fn
= xattr_tdb_connect
,
698 NTSTATUS
vfs_xattr_tdb_init(TALLOC_CTX
*ctx
)
700 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
, "xattr_tdb",