Initial draft xattr support
Initial, incomplete, support for extended attributes. Extended
attributes are implemented fairly naively, using a second table
in the file database using the primary file table's id as a
foreign key. The ON DELETE CASCADE behavior requires sqlite 3.6.19
or later with foreign key and trigger support compiled in.
To reduce round-trips, the client does not check for existing
attributes, but rather, sends three distinct set messages;
OP_SET_XATTR, OP_CREATE_XATTR, OP_REPLACE_XATTR. A SET message
always succeeds, a CREATE fails if the attribute already
exists, and a REPLACE fails if the attribute does not already
exist.
The /* flags */ feature of makewrappers is used to correct
path names appropriately, so all functions are already working
with complete paths, and can always use functions that work
on links; if they were supposed to dereference, the path
fixup code got that.
The xattr support is enabled, for now, conditional on
whether getfattr --help succeeds.
Not yet implemented: Translation for system.posix_acl_access,
which is used by "cp -a" (or "cp --preserve-all") on some
systems to try to copy modes.
Signed-off-by: Peter Seebach <peter.seebach@windriver.com>
Peter Seebach
10 years ago
24 | 24 | invoke, 'k', "invocation and launching" |
25 | 25 | benchmark, 'b', "performance statistics" |
26 | 26 | verbose, 'V', "extra detail" |
27 | xattr, 'x', "extended attributes" |
20 | 20 | may-unlink, 1 |
21 | 21 | did-unlink, 0 |
22 | 22 | cancel-unlink, 0 |
23 | get-xattr, 1 | |
24 | list-xattr, 1 | |
25 | remove-xattr, 1 | |
26 | set-xattr, 0 | |
27 | create-xattr, 1 | |
28 | replace-xattr, 1 |
0 | /* | |
1 | * Copyright (c) 2010 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * ssize_t fgetxattr(int filedes, const char *name, void *value, size_t size) | |
5 | * ssize_t rc = -1; | |
6 | */ | |
7 | ||
8 | /* suppress warnings */ | |
9 | (void) filedes; | |
10 | (void) name; | |
11 | (void) value; | |
12 | (void) size; | |
13 | errno = ENOTSUP; | |
14 | ||
15 | /* return rc; | |
16 | * } | |
17 | */ |
0 | /* | |
1 | * Copyright (c) 2010 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * ssize_t flistxattr(int filedes, char *list, size_t size) | |
5 | * ssize_t rc = -1; | |
6 | */ | |
7 | ||
8 | /* suppress warnings */ | |
9 | (void) filedes; | |
10 | (void) list; | |
11 | (void) size; | |
12 | errno = ENOTSUP; | |
13 | ||
14 | /* return rc; | |
15 | * } | |
16 | */ |
0 | /* | |
1 | * Copyright (c) 2010 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * int fremovexattr(int filedes, const char *name) | |
5 | * int rc = -1; | |
6 | */ | |
7 | ||
8 | /* suppress warnings */ | |
9 | (void) filedes; | |
10 | (void) name; | |
11 | errno = ENOTSUP; | |
12 | ||
13 | /* return rc; | |
14 | * } | |
15 | */ |
0 | /* | |
1 | * Copyright (c) 2010 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * int fsetxattr(int filedes, const char *name, const void *value, size_t size, int flags) | |
5 | * int rc = -1; | |
6 | */ | |
7 | ||
8 | /* suppress warnings */ | |
9 | (void) filedes; | |
10 | (void) name; | |
11 | (void) value; | |
12 | (void) size; | |
13 | (void) flags; | |
14 | errno = ENOTSUP; | |
15 | ||
16 | /* return rc; | |
17 | * } | |
18 | */ |
0 | /* | |
1 | * Copyright (c) 2010 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * ssize_t getxattr(const char *pathname, const char *name, void *value, size_t size) | |
5 | * ssize_t rc = -1; | |
6 | */ | |
7 | ||
8 | /* suppress warnings */ | |
9 | (void) pathname; | |
10 | (void) name; | |
11 | (void) value; | |
12 | (void) size; | |
13 | errno = ENOTSUP; | |
14 | ||
15 | /* return rc; | |
16 | * } | |
17 | */ |
0 | /* | |
1 | * Copyright (c) 2010 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * ssize_t lgetxattr(const char *pathname, const char *name, void *value, size_t size) | |
5 | * ssize_t rc = -1; | |
6 | */ | |
7 | ||
8 | /* suppress warnings */ | |
9 | (void) pathname; | |
10 | (void) name; | |
11 | (void) value; | |
12 | (void) size; | |
13 | errno = ENOTSUP; | |
14 | ||
15 | /* return rc; | |
16 | * } | |
17 | */ |
0 | /* | |
1 | * Copyright (c) 2010 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * ssize_t listxattr(const char *pathname, char *list, size_t size) | |
5 | * ssize_t rc = -1; | |
6 | */ | |
7 | ||
8 | /* suppress warnings */ | |
9 | (void) pathname; | |
10 | (void) list; | |
11 | (void) size; | |
12 | errno = ENOTSUP; | |
13 | ||
14 | /* return rc; | |
15 | * } | |
16 | */ |
0 | /* | |
1 | * Copyright (c) 2010 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * ssize_t llistxattr(const char *pathname, char *list, size_t size) | |
5 | * ssize_t rc = -1; | |
6 | */ | |
7 | ||
8 | /* suppress warnings */ | |
9 | (void) pathname; | |
10 | (void) list; | |
11 | (void) size; | |
12 | errno = ENOTSUP; | |
13 | ||
14 | /* return rc; | |
15 | * } | |
16 | */ |
0 | /* | |
1 | * Copyright (c) 2010 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * int lremovexattr(const char *pathname, const char *name) | |
5 | * int rc = -1; | |
6 | */ | |
7 | ||
8 | /* suppress warnings */ | |
9 | (void) pathname; | |
10 | (void) name; | |
11 | errno = ENOTSUP; | |
12 | ||
13 | /* return rc; | |
14 | * } | |
15 | */ |
0 | /* | |
1 | * Copyright (c) 2010 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * int lsetxattr(const char *pathname, const char *name, const void *value, size_t size, int flags) | |
5 | * int rc = -1; | |
6 | */ | |
7 | ||
8 | /* suppress warnings */ | |
9 | (void) pathname; | |
10 | (void) name; | |
11 | (void) value; | |
12 | (void) size; | |
13 | (void) flags; | |
14 | errno = ENOTSUP; | |
15 | ||
16 | /* return rc; | |
17 | * } | |
18 | */ |
0 | /* | |
1 | * Copyright (c) 2010 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * int removexattr(const char *pathname, const char *name) | |
5 | * int rc = -1; | |
6 | */ | |
7 | ||
8 | /* suppress warnings */ | |
9 | (void) pathname; | |
10 | (void) name; | |
11 | errno = ENOTSUP; | |
12 | ||
13 | /* return rc; | |
14 | * } | |
15 | */ |
0 | /* | |
1 | * Copyright (c) 2010 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * int setxattr(const char *pathname, const char *name, const void *value, size_t size, int flags) | |
5 | * int rc = -1; | |
6 | */ | |
7 | ||
8 | /* suppress warnings */ | |
9 | (void) pathname; | |
10 | (void) name; | |
11 | (void) value; | |
12 | (void) size; | |
13 | (void) flags; | |
14 | errno = ENOTSUP; | |
15 | ||
16 | /* return rc; | |
17 | * } | |
18 | */ |
0 | /* | |
1 | * Copyright (c) 2010 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * ssize_t fgetxattr(int filedes, const char *name, void *value, size_t size) | |
5 | * ssize_t rc = -1; | |
6 | */ | |
7 | ||
8 | /* suppress warnings */ | |
9 | (void) filedes; | |
10 | (void) name; | |
11 | (void) value; | |
12 | (void) size; | |
13 | errno = ENOTSUP; | |
14 | ||
15 | /* return rc; | |
16 | * } | |
17 | */ |
0 | /* | |
1 | * Copyright (c) 2010 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * ssize_t flistxattr(int filedes, char *list, size_t size) | |
5 | * ssize_t rc = -1; | |
6 | */ | |
7 | ||
8 | /* suppress warnings */ | |
9 | (void) filedes; | |
10 | (void) list; | |
11 | (void) size; | |
12 | errno = ENOTSUP; | |
13 | ||
14 | /* return rc; | |
15 | * } | |
16 | */ |
0 | /* | |
1 | * Copyright (c) 2010 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * int fremovexattr(int filedes, const char *name) | |
5 | * int rc = -1; | |
6 | */ | |
7 | ||
8 | /* suppress warnings */ | |
9 | (void) filedes; | |
10 | (void) name; | |
11 | errno = ENOTSUP; | |
12 | ||
13 | /* return rc; | |
14 | * } | |
15 | */ |
0 | /* | |
1 | * Copyright (c) 2010 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * int fsetxattr(int filedes, const char *name, const void *value, size_t size, int flags) | |
5 | * int rc = -1; | |
6 | */ | |
7 | ||
8 | /* suppress warnings */ | |
9 | (void) filedes; | |
10 | (void) name; | |
11 | (void) value; | |
12 | (void) size; | |
13 | (void) flags; | |
14 | errno = ENOTSUP; | |
15 | ||
16 | /* return rc; | |
17 | * } | |
18 | */ |
0 | /* | |
1 | * Copyright (c) 2010 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * ssize_t getxattr(const char *pathname, const char *name, void *value, size_t size) | |
5 | * ssize_t rc = -1; | |
6 | */ | |
7 | ||
8 | /* suppress warnings */ | |
9 | (void) pathname; | |
10 | (void) name; | |
11 | (void) value; | |
12 | (void) size; | |
13 | errno = ENOTSUP; | |
14 | ||
15 | /* return rc; | |
16 | * } | |
17 | */ |
0 | /* | |
1 | * Copyright (c) 2010 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * ssize_t lgetxattr(const char *pathname, const char *name, void *value, size_t size) | |
5 | * ssize_t rc = -1; | |
6 | */ | |
7 | ||
8 | /* suppress warnings */ | |
9 | (void) pathname; | |
10 | (void) name; | |
11 | (void) value; | |
12 | (void) size; | |
13 | errno = ENOTSUP; | |
14 | ||
15 | /* return rc; | |
16 | * } | |
17 | */ |
0 | /* | |
1 | * Copyright (c) 2010 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * ssize_t listxattr(const char *pathname, char *list, size_t size) | |
5 | * ssize_t rc = -1; | |
6 | */ | |
7 | ||
8 | /* suppress warnings */ | |
9 | (void) pathname; | |
10 | (void) list; | |
11 | (void) size; | |
12 | errno = ENOTSUP; | |
13 | ||
14 | /* return rc; | |
15 | * } | |
16 | */ |
0 | /* | |
1 | * Copyright (c) 2010 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * ssize_t llistxattr(const char *pathname, char *list, size_t size) | |
5 | * ssize_t rc = -1; | |
6 | */ | |
7 | ||
8 | /* suppress warnings */ | |
9 | (void) pathname; | |
10 | (void) list; | |
11 | (void) size; | |
12 | errno = ENOTSUP; | |
13 | ||
14 | /* return rc; | |
15 | * } | |
16 | */ |
0 | /* | |
1 | * Copyright (c) 2010 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * int lremovexattr(const char *pathname, const char *name) | |
5 | * int rc = -1; | |
6 | */ | |
7 | ||
8 | /* suppress warnings */ | |
9 | (void) pathname; | |
10 | (void) name; | |
11 | errno = ENOTSUP; | |
12 | ||
13 | /* return rc; | |
14 | * } | |
15 | */ |
0 | /* | |
1 | * Copyright (c) 2010 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * int lsetxattr(const char *pathname, const char *name, const void *value, size_t size, int flags) | |
5 | * int rc = -1; | |
6 | */ | |
7 | ||
8 | /* suppress warnings */ | |
9 | (void) pathname; | |
10 | (void) name; | |
11 | (void) value; | |
12 | (void) size; | |
13 | (void) flags; | |
14 | errno = ENOTSUP; | |
15 | ||
16 | /* return rc; | |
17 | * } | |
18 | */ |
0 | /* | |
1 | * Copyright (c) 2010 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * int removexattr(const char *pathname, const char *name) | |
5 | * int rc = -1; | |
6 | */ | |
7 | ||
8 | /* suppress warnings */ | |
9 | (void) pathname; | |
10 | (void) name; | |
11 | errno = ENOTSUP; | |
12 | ||
13 | /* return rc; | |
14 | * } | |
15 | */ |
0 | /* | |
1 | * Copyright (c) 2010 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * int setxattr(const char *pathname, const char *name, const void *value, size_t size, int flags) | |
5 | * int rc = -1; | |
6 | */ | |
7 | ||
8 | /* suppress warnings */ | |
9 | (void) pathname; | |
10 | (void) name; | |
11 | (void) value; | |
12 | (void) size; | |
13 | (void) flags; | |
14 | errno = ENOTSUP; | |
15 | ||
16 | /* return rc; | |
17 | * } | |
18 | */ |
0 | # we use "pathname" to avoid canonicalizing paths, because these functions are | |
1 | # unimplemented | |
2 | ssize_t getxattr(const char *pathname, const char *name, void *value, size_t size); | |
3 | ssize_t lgetxattr(const char *pathname, const char *name, void *value, size_t size); | |
4 | ssize_t fgetxattr(int filedes, const char *name, void *value, size_t size); | |
5 | ssize_t listxattr(const char *pathname, char *list, size_t size); | |
6 | ssize_t llistxattr(const char *pathname, char *list, size_t size); | |
7 | ssize_t flistxattr(int filedes, char *list, size_t size); | |
8 | int setxattr(const char *pathname, const char *name, const void *value, size_t size, int flags); | |
9 | int lsetxattr(const char *pathname, const char *name, const void *value, size_t size, int flags); | |
10 | int fsetxattr(int filedes, const char *name, const void *value, size_t size, int flags); | |
11 | int removexattr(const char *pathname, const char *name); | |
12 | int lremovexattr(const char *pathname, const char *name); | |
13 | int fremovexattr(int filedes, const char *name); |
24 | 24 | if ! $found; then |
25 | 25 | echo >&2 "Can't tell, omitting clone(2) support." |
26 | 26 | fi |
27 | if getfattr --help >/dev/null 2>&1; then | |
28 | echo "linux/xattr" | |
29 | else | |
30 | echo "linux/noxattr" | |
31 | fi |
12 | 12 | # just so we know the inums of symlinks |
13 | 13 | char *canonicalize_file_name(const char *filename); |
14 | 14 | int eaccess(const char *path, int mode); |
15 | # we use "pathname" to avoid canonicalizing paths, because these functions are | |
16 | # unimplemented | |
17 | ssize_t getxattr(const char *pathname, const char *name, void *value, size_t size); | |
18 | ssize_t lgetxattr(const char *pathname, const char *name, void *value, size_t size); | |
19 | ssize_t fgetxattr(int filedes, const char *name, void *value, size_t size); | |
20 | ssize_t listxattr(const char *pathname, char *list, size_t size); | |
21 | ssize_t llistxattr(const char *pathname, char *list, size_t size); | |
22 | ssize_t flistxattr(int filedes, char *list, size_t size); | |
23 | int setxattr(const char *pathname, const char *name, const void *value, size_t size, int flags); | |
24 | int lsetxattr(const char *pathname, const char *name, const void *value, size_t size, int flags); | |
25 | int fsetxattr(int filedes, const char *name, const void *value, size_t size, int flags); | |
26 | int removexattr(const char *pathname, const char *name); | |
27 | int lremovexattr(const char *pathname, const char *name); | |
28 | int fremovexattr(int filedes, const char *name); | |
29 | 15 | int open64(const char *path, int flags, ...{mode_t mode}); /* flags=0 */ |
30 | 16 | int openat64(int dirfd, const char *path, int flags, ...{mode_t mode}); /* flags=0 */ |
31 | 17 | int __openat64_2(int dirfd, const char *path, int flags); /* flags=0 */ |
0 | /* | |
1 | * Copyright (c) 2014 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * ssize_t fgetxattr(int filedes, const char *name, void *value, size_t size) | |
5 | * ssize_t rc = -1; | |
6 | */ | |
7 | rc = shared_getxattr(NULL, filedes, name, value, size); | |
8 | ||
9 | /* return rc; | |
10 | * } | |
11 | */ |
0 | /* | |
1 | * Copyright (c) 2014 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * ssize_t flistxattr(int filedes, char *list, size_t size) | |
5 | * ssize_t rc = -1; | |
6 | */ | |
7 | rc = shared_listxattr(NULL, filedes, list, size); | |
8 | ||
9 | /* return rc; | |
10 | * } | |
11 | */ |
0 | /* | |
1 | * Copyright (c) 2014 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * int fremovexattr(int filedes, const char *name) | |
5 | * int rc = -1; | |
6 | */ | |
7 | rc = shared_removexattr(NULL, filedes, name); | |
8 | ||
9 | /* return rc; | |
10 | * } | |
11 | */ |
0 | /* | |
1 | * Copyright (c) 2014 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * int fsetxattr(int filedes, const char *name, const void *value, size_t size, int flags) | |
5 | * int rc = -1; | |
6 | */ | |
7 | rc = shared_setxattr(NULL, filedes, name, value, size, flags); | |
8 | ||
9 | /* return rc; | |
10 | * } | |
11 | */ |
0 | /* | |
1 | * Copyright (c) 2014 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * ssize_t getxattr(const char *path, const char *name, void *value, size_t size) | |
5 | * ssize_t rc = -1; | |
6 | */ | |
7 | rc = shared_getxattr(path, -1, name, value, size); | |
8 | ||
9 | /* return rc; | |
10 | * } | |
11 | */ |
0 | /* | |
1 | * Copyright (c) 2014 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * ssize_t lgetxattr(const char *path, const char *name, void *value, size_t size) | |
5 | * ssize_t rc = -1; | |
6 | */ | |
7 | rc = shared_getxattr(path, -1, name, value, size); | |
8 | ||
9 | /* return rc; | |
10 | * } | |
11 | */ |
0 | /* | |
1 | * Copyright (c) 2014 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * ssize_t listxattr(const char *path, char *list, size_t size) | |
5 | * ssize_t rc = -1; | |
6 | */ | |
7 | rc = shared_listxattr(path, -1, list, size); | |
8 | ||
9 | /* return rc; | |
10 | * } | |
11 | */ |
0 | /* | |
1 | * Copyright (c) 2014 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * ssize_t llistxattr(const char *path, char *list, size_t size) | |
5 | * ssize_t rc = -1; | |
6 | */ | |
7 | rc = shared_listxattr(path, -1, list, size); | |
8 | ||
9 | /* return rc; | |
10 | * } | |
11 | */ |
0 | /* | |
1 | * Copyright (c) 2014 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * int lremovexattr(const char *path, const char *name) | |
5 | * int rc = -1; | |
6 | */ | |
7 | rc = shared_removexattr(path, -1, name); | |
8 | ||
9 | /* return rc; | |
10 | * } | |
11 | */ |
0 | /* | |
1 | * Copyright (c) 2014 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * int lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags) | |
5 | * int rc = -1; | |
6 | */ | |
7 | rc = shared_setxattr(path, -1, name, value, size, flags); | |
8 | ||
9 | /* return rc; | |
10 | * } | |
11 | */ |
0 | /* | |
1 | * Copyright (c) 2014 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * int removexattr(const char *path, const char *name) | |
5 | * int rc = -1; | |
6 | */ | |
7 | rc = shared_removexattr(path, -1, name); | |
8 | ||
9 | /* return rc; | |
10 | * } | |
11 | */ |
0 | /* | |
1 | * Copyright (c) 2014 Wind River Systems; see | |
2 | * guts/COPYRIGHT for information. | |
3 | * | |
4 | * int setxattr(const char *path, const char *name, const void *value, size_t size, int flags) | |
5 | * int rc = -1; | |
6 | */ | |
7 | rc = shared_setxattr(path, -1, name, value, size, flags); | |
8 | ||
9 | /* return rc; | |
10 | * } | |
11 | */ |
0 | #include <attr/xattr.h> |
0 | /* shared functionality for the xattr code */ | |
1 | /* Each of these functions is expecting to get an optional name, and | |
2 | * a populated statbuf to use for sending messages to the server. | |
3 | */ | |
4 | ||
5 | #define RC_AND_BUF \ | |
6 | int rc; \ | |
7 | PSEUDO_STATBUF buf; \ | |
8 | if (path) { \ | |
9 | rc = base_lstat(path, &buf); \ | |
10 | } else { \ | |
11 | rc = base_fstat(fd, &buf); \ | |
12 | } \ | |
13 | if (rc == -1) { \ | |
14 | return rc; \ | |
15 | } | |
16 | ||
17 | static ssize_t shared_getxattr(const char *path, int fd, const char *name, void *value, size_t size) { | |
18 | RC_AND_BUF | |
19 | ||
20 | pseudo_debug(PDBGF_XATTR, "getxattr(%s/%d, %s)\n", | |
21 | path ? path : "<no path>", fd, name); | |
22 | pseudo_msg_t *result = pseudo_client_op(OP_GET_XATTR, 0, fd, -1, path, &buf, name); | |
23 | if (result->result != RESULT_SUCCEED) { | |
24 | errno = ENOATTR; | |
25 | return -1; | |
26 | } | |
27 | ||
28 | if (value) { | |
29 | pseudo_debug(PDBGF_XATTR, "returned attributes: '%s' (%d bytes)\n", | |
30 | result->path, result->pathlen); | |
31 | if (size >= result->pathlen) { | |
32 | memcpy(value, result->path, result->pathlen); | |
33 | } else { | |
34 | memcpy(value, result->path, size); | |
35 | errno = ERANGE; | |
36 | } | |
37 | } | |
38 | return result->pathlen; | |
39 | } | |
40 | ||
41 | static int shared_setxattr(const char *path, int fd, const char *name, const void *value, size_t size, int flags) { | |
42 | RC_AND_BUF | |
43 | ||
44 | char *combined; | |
45 | size_t nlen = strlen(name); | |
46 | size_t combined_len = nlen + size + 1; | |
47 | combined = malloc(combined_len + 1); | |
48 | memcpy(combined, name, nlen); | |
49 | combined[nlen] = '\0'; | |
50 | memcpy(combined + nlen + 1, value, size); | |
51 | combined[combined_len] = '\0'; | |
52 | ||
53 | pseudo_debug(PDBGF_XATTR, "setxattr(%s/%d, %s, %s => %s [%d])\n", | |
54 | path ? path : "<no path>", fd, name, (char *) value, combined + nlen + 1, (int) size); | |
55 | ||
56 | pseudo_op_t op; | |
57 | switch (flags) { | |
58 | case XATTR_CREATE: | |
59 | op = OP_CREATE_XATTR; | |
60 | break; | |
61 | case XATTR_REPLACE: | |
62 | op = OP_REPLACE_XATTR; | |
63 | break; | |
64 | default: | |
65 | op = OP_SET_XATTR; | |
66 | break; | |
67 | } | |
68 | ||
69 | pseudo_msg_t *result = pseudo_client_op(op, 0, fd, -1, path, &buf, combined, combined_len); | |
70 | ||
71 | /* we automatically assume success */ | |
72 | if (op == OP_SET_XATTR) { | |
73 | return 0; | |
74 | } | |
75 | ||
76 | /* CREATE/REPLACE operations can report failure */ | |
77 | if (!result || result->result == RESULT_FAIL) { | |
78 | return -1; | |
79 | } | |
80 | ||
81 | return 0; | |
82 | } | |
83 | ||
84 | static ssize_t shared_listxattr(const char *path, int fd, char *list, size_t size) { | |
85 | RC_AND_BUF | |
86 | pseudo_msg_t *result = pseudo_client_op(OP_LIST_XATTR, 0, fd, -1, path, &buf); | |
87 | if (result->result != RESULT_SUCCEED) { | |
88 | pseudo_debug(PDBGF_XATTR, "listxattr: no success.\n"); | |
89 | errno = ENOATTR; | |
90 | return -1; | |
91 | } | |
92 | if (list) { | |
93 | pseudo_debug(PDBGF_XATTR, "listxattr: %d bytes of names, starting '%.*s'\n", | |
94 | (int) result->pathlen, (int) result->pathlen, result->path); | |
95 | if (size >= result->pathlen) { | |
96 | memcpy(list, result->path, result->pathlen); | |
97 | } else { | |
98 | memcpy(list, result->path, size); | |
99 | errno = ERANGE; | |
100 | } | |
101 | } | |
102 | return result->pathlen; | |
103 | } | |
104 | ||
105 | static int shared_removexattr(const char *path, int fd, const char *name) { | |
106 | RC_AND_BUF | |
107 | pseudo_msg_t *result = pseudo_client_op(OP_REMOVE_XATTR, 0, fd, -1, path, &buf, name); | |
108 | ||
109 | if (result->result != RESULT_SUCCEED) { | |
110 | /* docs say ENOATTR, but I don't have one */ | |
111 | errno = ENOENT; | |
112 | return -1; | |
113 | } | |
114 | return 0; | |
115 | } | |
116 |
0 | ssize_t getxattr(const char *path, const char *name, void *value, size_t size) /* flags=0 */; | |
1 | ssize_t lgetxattr(const char *path, const char *name, void *value, size_t size) /* flags=AT_SYMLINK_NOFOLLOW */; | |
2 | ssize_t fgetxattr(int filedes, const char *name, void *value, size_t size); | |
3 | int setxattr(const char *path, const char *name, const void *value, size_t size, int flags) /* flags=0 */; | |
4 | int lsetxattr(const char *path, const char *name, const void *value, size_t size, int flags) /* flags=AT_SYMLINK_NOFOLLOW */; | |
5 | int fsetxattr(int filedes, const char *name, const void *value, size_t size, int flags); | |
6 | ssize_t listxattr(const char *path, char *list, size_t size) /* flags=0 */; | |
7 | ssize_t llistxattr(const char *path, char *list, size_t size) /* flags=AT_SYMLINK_NOFOLLOW */; | |
8 | ssize_t flistxattr(int filedes, char *list, size_t size); | |
9 | int removexattr(const char *path, const char *name) /* flags=0 */; | |
10 | int lremovexattr(const char *path, const char *name) /* flags=AT_SYMLINK_NOFOLLOW */; | |
11 | int fremovexattr(int filedes, const char *name); |
50 | 50 | char *opt_r = NULL; |
51 | 51 | int opt_S = 0; |
52 | 52 | |
53 | static int pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag); | |
53 | static int pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag, char **response_path, size_t *response_len); | |
54 | 54 | static int pseudo_db_check(int fix); |
55 | 55 | |
56 | 56 | void |
317 | 317 | pseudo_diag("Couldn't allocate data structure for path.\n"); |
318 | 318 | exit(EXIT_FAILURE); |
319 | 319 | } |
320 | if (pdb_find_file_path(msg)) { | |
320 | if (pdb_find_file_path(msg, NULL)) { | |
321 | 321 | pseudo_diag("Couldn't find a database entry for '%s'.\n", opt_i); |
322 | 322 | exit(EXIT_FAILURE); |
323 | 323 | } |
484 | 484 | * sanity checks, then implements the fairly small DB changes required. |
485 | 485 | */ |
486 | 486 | int |
487 | pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag) { | |
487 | pseudo_op(pseudo_msg_t *msg, const char *program, const char *tag, char **response_path, size_t *response_len) { | |
488 | 488 | pseudo_msg_t msg_header; |
489 | 489 | pseudo_msg_t by_path = { .op = 0 }, by_ino = { .op = 0 }; |
490 | long long row = -1; | |
490 | 491 | pseudo_msg_t db_header; |
491 | 492 | char *path_by_ino = 0; |
492 | 493 | char *oldpath = 0; |
494 | size_t oldpathlen = 0; | |
493 | 495 | int found_path = 0, found_ino = 0; |
494 | 496 | int prefer_ino = 0; |
497 | int xattr_flags = 0; | |
495 | 498 | |
496 | 499 | if (!msg) |
497 | 500 | return 1; |
519 | 522 | * stuff into a rename, break them apart (null seperated) |
520 | 523 | */ |
521 | 524 | |
522 | if (msg->pathlen && msg->op == OP_RENAME) { | |
523 | /* In a rename there are two paths, null seperate in msg->path */ | |
524 | oldpath = msg->path + strlen(msg->path) + 1; | |
525 | pseudo_debug(PDBGF_OP | PDBGF_FILE, "rename: path %s, oldpath %s\n", | |
526 | msg->path, oldpath); | |
525 | if (msg->pathlen) { | |
526 | switch (msg->op) { | |
527 | case OP_RENAME: | |
528 | case OP_CREATE_XATTR: | |
529 | case OP_GET_XATTR: | |
530 | case OP_LIST_XATTR: | |
531 | case OP_REPLACE_XATTR: | |
532 | case OP_SET_XATTR: | |
533 | /* In a rename there are two paths, null separated in msg->path */ | |
534 | oldpath = msg->path + strlen(msg->path) + 1; | |
535 | oldpathlen = msg->pathlen - (oldpath - msg->path); | |
536 | pseudo_debug(PDBGF_OP | PDBGF_FILE | PDBGF_XATTR, "%s: path '%s', oldpath '%s' [%d]\n", | |
537 | pseudo_op_name(msg->op), msg->path, oldpath, (int) oldpathlen); | |
538 | break; | |
539 | default: | |
540 | break; | |
541 | } | |
527 | 542 | } |
528 | 543 | |
529 | 544 | /* stash original header, in case we need it later */ |
536 | 551 | |
537 | 552 | /* Lookup the full path, with inode and dev if available */ |
538 | 553 | if (msg->pathlen && msg->dev && msg->ino) { |
539 | if (!pdb_find_file_exact(msg)) { | |
554 | if (!pdb_find_file_exact(msg, &row)) { | |
540 | 555 | /* restore header contents */ |
541 | 556 | by_path = *msg; |
542 | 557 | by_ino = *msg; |
552 | 567 | if (msg->pathlen) { |
553 | 568 | /* for now, don't canonicalize paths anymore */ |
554 | 569 | /* used to do it here, but now doing it in client */ |
555 | if (!pdb_find_file_path(msg)) { | |
570 | if (!pdb_find_file_path(msg, &row)) { | |
556 | 571 | by_path = *msg; |
557 | 572 | found_path = 1; |
558 | 573 | } else { |
563 | 578 | } |
564 | 579 | /* search on original inode -- in case of mismatch */ |
565 | 580 | if (msg->dev && msg->ino) { |
566 | if (!pdb_find_file_dev(&by_ino)) { | |
581 | if (!pdb_find_file_dev(&by_ino, &row)) { | |
567 | 582 | found_ino = 1; |
568 | 583 | path_by_ino = pdb_get_file_path(&by_ino); |
569 | 584 | } |
759 | 774 | pdb_unlink_file_dev(&by_ino); |
760 | 775 | } |
761 | 776 | if (!found_path) { |
762 | pdb_link_file(msg); | |
777 | pdb_link_file(msg, NULL); | |
763 | 778 | } else { |
764 | 779 | /* again, an error, but leaving it alone for now. */ |
765 | 780 | pseudo_diag("creat ignored for existing file '%s'.\n", |
789 | 804 | /* if the path is not known, link it */ |
790 | 805 | if (!found_path) { |
791 | 806 | pseudo_debug(PDBGF_FILE, "(new) "); |
792 | pdb_link_file(msg); | |
807 | pdb_link_file(msg, NULL); | |
793 | 808 | } |
794 | 809 | break; |
795 | 810 | case OP_CHOWN: /* FALLTHROUGH */ |
815 | 830 | } |
816 | 831 | /* if the path is not known, link it */ |
817 | 832 | if (!found_path) { |
818 | pdb_link_file(msg); | |
833 | pdb_link_file(msg, NULL); | |
819 | 834 | } |
820 | 835 | break; |
821 | 836 | case OP_STAT: /* FALLTHROUGH */ |
870 | 885 | } else { |
871 | 886 | *msg = msg_header; |
872 | 887 | } |
873 | pdb_link_file(msg); | |
888 | pdb_link_file(msg, NULL); | |
874 | 889 | break; |
875 | 890 | case OP_RENAME: |
876 | 891 | /* a rename implies renaming an existing entry... and every |
932 | 947 | pdb_unlink_file_dev(&by_ino); |
933 | 948 | } |
934 | 949 | *msg = msg_header; |
935 | pdb_link_file(msg); | |
950 | pdb_link_file(msg, NULL); | |
951 | break; | |
952 | case OP_GET_XATTR: | |
953 | if (pdb_get_xattr(row, &oldpath, &oldpathlen)) { | |
954 | msg->result = RESULT_FAIL; | |
955 | } else { | |
956 | *response_path = oldpath; | |
957 | *response_len = oldpathlen; | |
958 | pseudo_debug(PDBGF_XATTR, "get results: '%s' (%d bytes)\n", | |
959 | *response_path, (int) *response_len); | |
960 | } | |
961 | break; | |
962 | case OP_LIST_XATTR: | |
963 | if (pdb_list_xattr(row, &oldpath, &oldpathlen)) { | |
964 | msg->result = RESULT_FAIL; | |
965 | } else { | |
966 | pseudo_debug(PDBGF_XATTR, "got %d bytes of xattrs to list: %.*s\n", (int) oldpathlen, (int) oldpathlen, oldpath); | |
967 | *response_path = oldpath; | |
968 | *response_len = oldpathlen; | |
969 | } | |
970 | break; | |
971 | case OP_CREATE_XATTR: | |
972 | case OP_REPLACE_XATTR: /* fallthrough */ | |
973 | if (msg->op == OP_CREATE_XATTR) { | |
974 | xattr_flags = XATTR_CREATE; | |
975 | } | |
976 | if (msg->op == OP_REPLACE_XATTR) { | |
977 | xattr_flags = XATTR_REPLACE; | |
978 | } | |
979 | case OP_SET_XATTR: | |
980 | /* we need a row entry to store xattr info */ | |
981 | if (row == -1) { | |
982 | pdb_link_file(msg, &row); | |
983 | } | |
984 | if (pdb_set_xattr(row, oldpath, oldpathlen, xattr_flags)) { | |
985 | msg->result = RESULT_FAIL; | |
986 | } | |
987 | break; | |
988 | case OP_REMOVE_XATTR: | |
989 | pdb_remove_xattr(row, oldpath, oldpathlen); | |
936 | 990 | break; |
937 | 991 | default: |
938 | 992 | pseudo_diag("unknown op from client %d, op %d [%s]\n", |
955 | 1009 | |
956 | 1010 | /* SHUTDOWN does not get this far, it's handled in pseudo_server.c */ |
957 | 1011 | int |
958 | pseudo_server_response(pseudo_msg_t *msg, const char *program, const char *tag) { | |
1012 | pseudo_server_response(pseudo_msg_t *msg, const char *program, const char *tag, char **response_path, size_t *response_len) { | |
959 | 1013 | switch (msg->type) { |
960 | 1014 | case PSEUDO_MSG_PING: |
961 | 1015 | msg->result = RESULT_SUCCEED; |
965 | 1019 | break; |
966 | 1020 | case PSEUDO_MSG_OP: |
967 | 1021 | case PSEUDO_MSG_FASTOP: |
968 | return pseudo_op(msg, program, tag); | |
1022 | return pseudo_op(msg, program, tag, response_path, response_len); | |
969 | 1023 | break; |
970 | 1024 | case PSEUDO_MSG_ACK: /* FALLTHROUGH */ |
971 | 1025 | case PSEUDO_MSG_NAK: /* FALLTHROUGH */ |
133 | 133 | */ |
134 | 134 | #define PSEUDO_LINK_SYMLINK_BEHAVIOR 0 |
135 | 135 | |
136 | /* given n, pick a multiple of block enough bigger than n | |
137 | * to give us some breathing room. | |
138 | */ | |
139 | static inline size_t | |
140 | round_up(size_t n, size_t block) { | |
141 | return block * (((n + block / 4) / block) + 1); | |
142 | } | |
143 | ||
136 | 144 | #include "pseudo_ports.h" |
1055 | 1055 | size_t pathlen = -1; |
1056 | 1056 | int do_request = 0; |
1057 | 1057 | char *oldpath = 0; |
1058 | size_t oldpathlen = 0; | |
1058 | 1059 | char *alloced_path = 0; |
1059 | 1060 | |
1060 | 1061 | /* disable wrappers */ |
1072 | 1073 | pseudo_magic(); |
1073 | 1074 | return 0; |
1074 | 1075 | } |
1076 | /* we have to calculate this here, because SET_XATTR | |
1077 | * and friends will be using oldpath to hold a hunk of | |
1078 | * data of arbitrary length | |
1079 | */ | |
1080 | oldpathlen = strlen(oldpath); | |
1075 | 1081 | if (!path) { |
1076 | 1082 | pseudo_diag("rename (%s) without new path.\n", |
1077 | 1083 | path ? path : "<nil>"); |
1078 | 1084 | pseudo_magic(); |
1079 | 1085 | return 0; |
1080 | 1086 | } |
1087 | } | |
1088 | ||
1089 | /* we treat the "create" and "replace" flags as logically | |
1090 | * distinct operations, because they can fail when set can't. | |
1091 | */ | |
1092 | if (op == OP_SET_XATTR || op == OP_CREATE_XATTR || op == OP_REPLACE_XATTR) { | |
1093 | va_list ap; | |
1094 | va_start(ap, buf); | |
1095 | oldpath = va_arg(ap, char *); | |
1096 | oldpathlen = va_arg(ap, size_t); | |
1097 | pseudo_debug(PDBGF_XATTR, "setxattr, oldpath (%d bytes): '%s'\n", | |
1098 | (int) oldpathlen, oldpath); | |
1099 | va_end(ap); | |
1100 | } | |
1101 | if (op == OP_GET_XATTR){ | |
1102 | va_list ap; | |
1103 | va_start(ap, buf); | |
1104 | oldpath = va_arg(ap, char *); | |
1105 | oldpathlen = strlen(oldpath); | |
1106 | pseudo_debug(PDBGF_XATTR, "getxattr, oldpath (%d bytes): '%s'\n", | |
1107 | (int) oldpathlen, oldpath); | |
1108 | va_end(ap); | |
1081 | 1109 | } |
1082 | 1110 | |
1083 | 1111 | if (path) { |
1092 | 1120 | pathlen = strlen(path) + 1; |
1093 | 1121 | int strip_slash = (pathlen > 2 && (path[pathlen - 2]) == '/'); |
1094 | 1122 | if (oldpath) { |
1095 | size_t full_len = strlen(oldpath) + 1 + pathlen; | |
1123 | size_t full_len = oldpathlen + 1 + pathlen; | |
1124 | size_t partial_len = pathlen - 1 - strip_slash; | |
1096 | 1125 | char *both_paths = malloc(full_len); |
1097 | 1126 | if (!both_paths) { |
1098 | 1127 | pseudo_diag("Can't allocate space for paths for a rename operation. Sorry.\n"); |
1099 | 1128 | pseudo_magic(); |
1100 | 1129 | return 0; |
1101 | 1130 | } |
1102 | snprintf(both_paths, full_len, "%.*s%c%s", | |
1103 | (int) (pathlen - 1 - strip_slash), | |
1104 | path, 0, oldpath); | |
1131 | memcpy(both_paths, path, partial_len); | |
1132 | both_paths[partial_len] = '\0'; | |
1133 | memcpy(both_paths + partial_len + 1, oldpath, oldpathlen); | |
1134 | both_paths[full_len - 1] = '\0'; | |
1105 | 1135 | pseudo_debug(PDBGF_PATH | PDBGF_FILE, "rename: %s -> %s [%d]\n", |
1106 | 1136 | both_paths + pathlen, both_paths, (int) full_len); |
1107 | 1137 | alloced_path = both_paths; |
1238 | 1268 | case OP_DID_UNLINK: |
1239 | 1269 | case OP_CANCEL_UNLINK: |
1240 | 1270 | case OP_MAY_UNLINK: |
1271 | case OP_GET_XATTR: | |
1272 | case OP_LIST_XATTR: | |
1273 | case OP_SET_XATTR: | |
1274 | case OP_REMOVE_XATTR: | |
1241 | 1275 | do_request = 1; |
1242 | 1276 | break; |
1243 | 1277 | default: |
84 | 84 | "rdev INTEGER", |
85 | 85 | NULL, |
86 | 86 | NULL }, |
87 | { "xattrs", | |
88 | "id INTEGER PRIMARY KEY, " | |
89 | "file_id INTEGER REFERENCES files(id) ON DELETE CASCADE, " | |
90 | "name VARCHAR, " | |
91 | "value VARCHAR", | |
92 | NULL, | |
93 | NULL }, | |
87 | 94 | { NULL, NULL, NULL, NULL }, |
88 | 95 | }, log_tables[] = { |
89 | 96 | { "logs", |
113 | 120 | /* { "files__path", "files", "path" }, */ |
114 | 121 | { "files__path_dev_ino", "files", "path, dev, ino" }, |
115 | 122 | { "files__dev_ino", "files", "dev, ino" }, |
123 | { "xattrs__file", "xattrs", "file_id" }, | |
116 | 124 | { NULL, NULL, NULL }, |
117 | 125 | }, log_indexes[] = { |
118 | 126 | { NULL, NULL, NULL }, |
135 | 143 | * need. |
136 | 144 | */ |
137 | 145 | "PRAGMA synchronous = OFF;", |
146 | "PRAGMA foreign_keys = ON;", | |
138 | 147 | NULL |
139 | 148 | }; |
140 | 149 | |
364 | 373 | |
365 | 374 | for (i = 0; sql_tables[i].name; ++i) { |
366 | 375 | found = 0; |
376 | printf("considering table %s\n", sql_tables[i].name); | |
367 | 377 | for (j = 1; j <= rows; ++j) { |
368 | 378 | if (!strcmp(existing[j], sql_tables[i].name)) { |
369 | 379 | found = 1; |
586 | 596 | if (dbinfo->pragmas) { |
587 | 597 | for (i = 0; dbinfo->pragmas[i]; ++i) { |
588 | 598 | rc = sqlite3_exec(db, dbinfo->pragmas[i], NULL, NULL, &errmsg); |
599 | pseudo_debug(PDBGF_SQL | PDBGF_VERBOSE, "executed pragma: '%s', rc %d.\n", | |
600 | dbinfo->pragmas[i], rc); | |
589 | 601 | if (rc) { |
590 | 602 | dberr(db, dbinfo->pragmas[i]); |
591 | 603 | } |
1355 | 1367 | * or 'NAMELESS FILE'. |
1356 | 1368 | */ |
1357 | 1369 | int |
1358 | pdb_link_file(pseudo_msg_t *msg) { | |
1370 | pdb_link_file(pseudo_msg_t *msg, long long *row) { | |
1359 | 1371 | static sqlite3_stmt *insert; |
1360 | 1372 | int rc; |
1361 | 1373 | char *sql = "INSERT INTO files " |
1396 | 1408 | if (rc != SQLITE_DONE) { |
1397 | 1409 | dberr(file_db, "insert may have failed (rc %d)", rc); |
1398 | 1410 | } |
1411 | /* some users care what the row ID is */ | |
1412 | if (row) { | |
1413 | *row = sqlite3_last_insert_rowid(file_db); | |
1414 | } | |
1399 | 1415 | sqlite3_reset(insert); |
1400 | 1416 | sqlite3_clear_bindings(insert); |
1401 | 1417 | return rc != SQLITE_DONE; |
1932 | 1948 | |
1933 | 1949 | /* find file using both path AND dev/inode as key */ |
1934 | 1950 | int |
1935 | pdb_find_file_exact(pseudo_msg_t *msg) { | |
1951 | pdb_find_file_exact(pseudo_msg_t *msg, long long *row) { | |
1936 | 1952 | static sqlite3_stmt *select; |
1937 | 1953 | int rc; |
1938 | 1954 | char *sql = "SELECT * FROM files WHERE path = ? AND dev = ? AND ino = ?;"; |
1960 | 1976 | rc = sqlite3_step(select); |
1961 | 1977 | switch (rc) { |
1962 | 1978 | case SQLITE_ROW: |
1979 | if (row) { | |
1980 | *row = sqlite3_column_int64(select, 0); | |
1981 | } | |
1963 | 1982 | msg->uid = (unsigned long) sqlite3_column_int64(select, 4); |
1964 | 1983 | msg->gid = (unsigned long) sqlite3_column_int64(select, 5); |
1965 | 1984 | msg->mode = (unsigned long) sqlite3_column_int64(select, 6); |
1983 | 2002 | |
1984 | 2003 | /* find file using path as a key */ |
1985 | 2004 | int |
1986 | pdb_find_file_path(pseudo_msg_t *msg) { | |
2005 | pdb_find_file_path(pseudo_msg_t *msg, long long *row) { | |
1987 | 2006 | static sqlite3_stmt *select; |
1988 | 2007 | int rc; |
1989 | 2008 | char *sql = "SELECT * FROM files WHERE path = ?;"; |
2014 | 2033 | rc = sqlite3_step(select); |
2015 | 2034 | switch (rc) { |
2016 | 2035 | case SQLITE_ROW: |
2036 | if (row) { | |
2037 | *row = sqlite3_column_int64(select, 0); | |
2038 | } | |
2017 | 2039 | msg->dev = sqlite3_column_int64(select, 2); |
2018 | 2040 | msg->ino = sqlite3_column_int64(select, 3); |
2019 | 2041 | msg->uid = sqlite3_column_int64(select, 4); |
2089 | 2111 | |
2090 | 2112 | /* find file using dev/inode as key */ |
2091 | 2113 | int |
2092 | pdb_find_file_dev(pseudo_msg_t *msg) { | |
2114 | pdb_find_file_dev(pseudo_msg_t *msg, long long *row) { | |
2093 | 2115 | static sqlite3_stmt *select; |
2094 | 2116 | int rc; |
2095 | 2117 | char *sql = "SELECT * FROM files WHERE dev = ? AND ino = ?;"; |
2113 | 2135 | rc = sqlite3_step(select); |
2114 | 2136 | switch (rc) { |
2115 | 2137 | case SQLITE_ROW: |
2138 | if (row) { | |
2139 | *row = sqlite3_column_int64(select, 0); | |
2140 | } | |
2116 | 2141 | msg->uid = (unsigned long) sqlite3_column_int64(select, 4); |
2117 | 2142 | msg->gid = (unsigned long) sqlite3_column_int64(select, 5); |
2118 | 2143 | msg->mode = (unsigned long) sqlite3_column_int64(select, 6); |
2134 | 2159 | return rc; |
2135 | 2160 | } |
2136 | 2161 | |
2162 | int | |
2163 | pdb_get_xattr(long long file_id, char **value, size_t *len) { | |
2164 | static sqlite3_stmt *select; | |
2165 | int rc; | |
2166 | char *response; | |
2167 | size_t length; | |
2168 | char *sql = "SELECT value FROM xattrs WHERE file_id = ? AND name = ?;"; | |
2169 | ||
2170 | if (!file_db && get_dbs()) { | |
2171 | pseudo_diag("%s: database error.\n", __func__); | |
2172 | return 0; | |
2173 | } | |
2174 | if (!select) { | |
2175 | rc = sqlite3_prepare_v2(file_db, sql, strlen(sql), &select, NULL); | |
2176 | if (rc) { | |
2177 | dberr(file_db, "couldn't prepare SELECT statement"); | |
2178 | return 1; | |
2179 | } | |
2180 | } | |
2181 | pseudo_debug(PDBGF_XATTR, "requested xattr named '%s' for file %lld\n", *value, file_id); | |
2182 | sqlite3_bind_int(select, 1, file_id); | |
2183 | rc = sqlite3_bind_text(select, 2, *value, -1, SQLITE_STATIC); | |
2184 | if (rc) { | |
2185 | dberr(file_db, "couldn't bind xattr name to SELECT."); | |
2186 | return 1; | |
2187 | } | |
2188 | rc = sqlite3_step(select); | |
2189 | switch (rc) { | |
2190 | case SQLITE_ROW: | |
2191 | response = (char *) sqlite3_column_text(select, 0); | |
2192 | length = sqlite3_column_bytes(select, 0); | |
2193 | pseudo_debug(PDBGF_XATTR, "got %d-byte results: '%s'\n", | |
2194 | (int) length, response); | |
2195 | if (response && length >= 1) { | |
2196 | /* not a strdup because the values can contain | |
2197 | * arbitrary bytes. | |
2198 | */ | |
2199 | *value = malloc(length); | |
2200 | memcpy(*value, response, length); | |
2201 | *len = length; | |
2202 | rc = 0; | |
2203 | } else { | |
2204 | *value = NULL; | |
2205 | *len = 0; | |
2206 | rc = 1; | |
2207 | } | |
2208 | break; | |
2209 | case SQLITE_DONE: | |
2210 | pseudo_debug(PDBGF_DB, "find_exact: sqlite_done on first row\n"); | |
2211 | rc = 1; | |
2212 | break; | |
2213 | default: | |
2214 | dberr(file_db, "find_exact: select returned neither a row nor done"); | |
2215 | rc = 1; | |
2216 | break; | |
2217 | } | |
2218 | sqlite3_reset(select); | |
2219 | sqlite3_clear_bindings(select); | |
2220 | return rc; | |
2221 | } | |
2222 | ||
2223 | int | |
2224 | pdb_list_xattr(long long file_id, char **value, size_t *len) { | |
2225 | static sqlite3_stmt *select; | |
2226 | size_t allocated = 0; | |
2227 | size_t used = 0; | |
2228 | char *buffer = 0; | |
2229 | int rc; | |
2230 | char *sql = "SELECT name FROM xattrs WHERE file_id = ?;"; | |
2231 | ||
2232 | /* if we don't have a record of the file, it must not have | |
2233 | * any extended attributes... | |
2234 | */ | |
2235 | if (file_id == -1) { | |
2236 | *value = NULL; | |
2237 | *len = 0; | |
2238 | return 0; | |
2239 | } | |
2240 | ||
2241 | if (!file_db && get_dbs()) { | |
2242 | pseudo_diag("%s: database error.\n", __func__); | |
2243 | return 0; | |
2244 | } | |
2245 | if (!select) { | |
2246 | rc = sqlite3_prepare_v2(file_db, sql, strlen(sql), &select, NULL); | |
2247 | if (rc) { | |
2248 | dberr(file_db, "couldn't prepare SELECT statement"); | |
2249 | return 1; | |
2250 | } | |
2251 | } | |
2252 | sqlite3_bind_int(select, 1, file_id); | |
2253 | do { | |
2254 | rc = sqlite3_step(select); | |
2255 | if (rc == SQLITE_ROW) { | |
2256 | char *value = (char *) sqlite3_column_text(select, 0); | |
2257 | size_t len = sqlite3_column_bytes(select, 0); | |
2258 | if (!buffer) { | |
2259 | allocated = round_up(len, 256); | |
2260 | buffer = malloc(allocated); | |
2261 | } | |
2262 | if (used + len + 2 > allocated) { | |
2263 | size_t new_allocated = round_up(used + len + 2, 256); | |
2264 | char *new_buffer = malloc(new_allocated); | |
2265 | memcpy(new_buffer, buffer, used); | |
2266 | free(buffer); | |
2267 | allocated = new_allocated; | |
2268 | buffer = new_buffer; | |
2269 | } | |
2270 | memcpy(buffer + used, value, len); | |
2271 | buffer[used + len] = '\0'; | |
2272 | used = used + len + 1; | |
2273 | } else if (rc == SQLITE_DONE) { | |
2274 | *value = buffer; | |
2275 | *len = used; | |
2276 | } else { | |
2277 | dberr(file_db, "non-row response from select?"); | |
2278 | free(buffer); | |
2279 | *value = NULL; | |
2280 | *len = 0; | |
2281 | } | |
2282 | } while (rc == SQLITE_ROW); | |
2283 | sqlite3_reset(select); | |
2284 | sqlite3_clear_bindings(select); | |
2285 | return rc != SQLITE_DONE; | |
2286 | } | |
2287 | ||
2288 | int | |
2289 | pdb_remove_xattr(long long file_id, char *value, size_t len) { | |
2290 | static sqlite3_stmt *delete; | |
2291 | int rc; | |
2292 | char *sql = "DELETE FROM xattrs WHERE file_id = ? AND name = ?;"; | |
2293 | ||
2294 | if (!file_db && get_dbs()) { | |
2295 | pseudo_diag("%s: database error.\n", __func__); | |
2296 | return 0; | |
2297 | } | |
2298 | if (!delete) { | |
2299 | rc = sqlite3_prepare_v2(file_db, sql, strlen(sql), &delete, NULL); | |
2300 | if (rc) { | |
2301 | dberr(file_db, "couldn't prepare DELETE statement"); | |
2302 | return 1; | |
2303 | } | |
2304 | } | |
2305 | sqlite3_bind_int(delete, 1, file_id); | |
2306 | rc = sqlite3_bind_text(delete, 2, value, len, SQLITE_STATIC); | |
2307 | if (rc) { | |
2308 | dberr(file_db, "couldn't bind xattr name to DELETE."); | |
2309 | return 1; | |
2310 | } | |
2311 | file_db_dirty = 1; | |
2312 | rc = sqlite3_step(delete); | |
2313 | if (rc != SQLITE_DONE) { | |
2314 | dberr(file_db, "delete xattr may have failed"); | |
2315 | } | |
2316 | sqlite3_reset(delete); | |
2317 | sqlite3_clear_bindings(delete); | |
2318 | return rc != SQLITE_DONE; | |
2319 | } | |
2320 | ||
2321 | int | |
2322 | pdb_set_xattr(long long file_id, char *value, size_t len, int flags) { | |
2323 | static sqlite3_stmt *select, *update, *insert; | |
2324 | int rc; | |
2325 | long long existing_row = -1; | |
2326 | char *select_sql = "SELECT id FROM xattrs WHERE file_id = ? AND name = ?;"; | |
2327 | char *insert_sql = "INSERT INTO xattrs " | |
2328 | " ( file_id, name, value ) " | |
2329 | " VALUES (?, ?, ?);"; | |
2330 | char *update_sql = "UPDATE xattrs SET value = ? WHERE id = ?;"; | |
2331 | char *vname = value; | |
2332 | size_t vlen; | |
2333 | ||
2334 | if (!file_db && get_dbs()) { | |
2335 | pseudo_diag("%s: database error.\n", __func__); | |
2336 | return 0; | |
2337 | } | |
2338 | if (!select) { | |
2339 | rc = sqlite3_prepare_v2(file_db, select_sql, strlen(select_sql), &select, NULL); | |
2340 | if (rc) { | |
2341 | dberr(file_db, "couldn't prepare SELECT statement"); | |
2342 | return 1; | |
2343 | } | |
2344 | } | |
2345 | sqlite3_bind_int(select, 1, file_id); | |
2346 | rc = sqlite3_bind_text(select, 2, value, -1, SQLITE_STATIC); | |
2347 | if (rc) { | |
2348 | dberr(file_db, "couldn't bind xattr name to SELECT."); | |
2349 | return 1; | |
2350 | } | |
2351 | rc = sqlite3_step(select); | |
2352 | switch (rc) { | |
2353 | case SQLITE_ROW: | |
2354 | existing_row = sqlite3_column_int64(select, 0); | |
2355 | break; | |
2356 | case SQLITE_DONE: | |
2357 | pseudo_debug(PDBGF_DB | PDBGF_VERBOSE, "find_exact: sqlite_done on first row\n"); | |
2358 | existing_row = -1; | |
2359 | break; | |
2360 | default: | |
2361 | dberr(file_db, "set_xattr: select returned neither a row nor done"); | |
2362 | rc = 1; | |
2363 | break; | |
2364 | } | |
2365 | sqlite3_reset(select); | |
2366 | sqlite3_clear_bindings(select); | |
2367 | if (flags == XATTR_CREATE && existing_row != -1) { | |
2368 | pseudo_debug(PDBGF_DB, "XATTR_CREATE with an existing row, failing."); | |
2369 | return 1; | |
2370 | } | |
2371 | if (flags == XATTR_REPLACE && existing_row == -1) { | |
2372 | pseudo_debug(PDBGF_DB, "XATTR_REPLACE with no existing row, failing."); | |
2373 | return 1; | |
2374 | } | |
2375 | /* the material after the name is the value */ | |
2376 | vlen = strlen(value); | |
2377 | len = len - (vlen + 1); | |
2378 | value = value + len; | |
2379 | pseudo_debug(PDBGF_XATTR, "trying to set a value for %lld: name is '%s', value is '%s'. Existing row %lld.\n", | |
2380 | file_id, vname, value, existing_row); | |
2381 | if (existing_row != -1) { | |
2382 | /* update */ | |
2383 | if (!update) { | |
2384 | rc = sqlite3_prepare_v2(file_db, update_sql, strlen(update_sql), &update, NULL); | |
2385 | if (rc) { | |
2386 | dberr(file_db, "couldn't prepare UPDATE statement"); | |
2387 | return 1; | |
2388 | } | |
2389 | } | |
2390 | rc = sqlite3_bind_text(update, 1, value, -1, SQLITE_STATIC); | |
2391 | if (rc) { | |
2392 | dberr(file_db, "couldn't bind xattr value to UPDATE."); | |
2393 | return 1; | |
2394 | } | |
2395 | sqlite3_bind_int(update, 2, existing_row); | |
2396 | file_db_dirty = 1; | |
2397 | rc = sqlite3_step(update); | |
2398 | if (rc != SQLITE_DONE) { | |
2399 | dberr(file_db, "update xattr may have failed"); | |
2400 | } | |
2401 | sqlite3_reset(update); | |
2402 | sqlite3_clear_bindings(update); | |
2403 | return rc != SQLITE_DONE; | |
2404 | } else { | |
2405 | /* insert */ | |
2406 | if (!insert) { | |
2407 | rc = sqlite3_prepare_v2(file_db, insert_sql, strlen(insert_sql), &insert, NULL); | |
2408 | if (rc) { | |
2409 | dberr(file_db, "couldn't prepare INSERT statement"); | |
2410 | return 1; | |
2411 | } | |
2412 | } | |
2413 | pseudo_debug(PDBGF_XATTR, "insert should be getting ID %lld\n", file_id); | |
2414 | sqlite3_bind_int64(insert, 1, file_id); | |
2415 | rc = sqlite3_bind_text(insert, 2, vname, -1, SQLITE_STATIC); | |
2416 | if (rc) { | |
2417 | dberr(file_db, "couldn't bind xattr name to INSERT statement"); | |
2418 | return 1; | |
2419 | } | |
2420 | rc = sqlite3_bind_text(insert, 3, value, vlen, SQLITE_STATIC); | |
2421 | if (rc) { | |
2422 | dberr(file_db, "couldn't bind xattr value to INSERT statement"); | |
2423 | return 1; | |
2424 | } | |
2425 | file_db_dirty = 1; | |
2426 | rc = sqlite3_step(insert); | |
2427 | if (rc != SQLITE_DONE) { | |
2428 | dberr(file_db, "insert xattr may have failed"); | |
2429 | } | |
2430 | sqlite3_reset(insert); | |
2431 | sqlite3_clear_bindings(insert); | |
2432 | return rc != SQLITE_DONE; | |
2433 | } | |
2434 | return rc; | |
2435 | } | |
2436 | ||
2137 | 2437 | /* find file using only inode as key. Unused for now, planned to come |
2138 | 2438 | * in for NFS usage. |
2139 | 2439 | */ |
2140 | 2440 | int |
2141 | pdb_find_file_ino(pseudo_msg_t *msg) { | |
2441 | pdb_find_file_ino(pseudo_msg_t *msg, long long *row) { | |
2142 | 2442 | static sqlite3_stmt *select; |
2143 | 2443 | int rc; |
2144 | 2444 | char *sql = "SELECT * FROM files WHERE ino = ?;"; |
2161 | 2461 | rc = sqlite3_step(select); |
2162 | 2462 | switch (rc) { |
2163 | 2463 | case SQLITE_ROW: |
2464 | if (row) { | |
2465 | *row = sqlite3_column_int64(select, 0); | |
2466 | } | |
2164 | 2467 | msg->dev = (unsigned long) sqlite3_column_int64(select, 2); |
2165 | 2468 | msg->uid = (unsigned long) sqlite3_column_int64(select, 4); |
2166 | 2469 | msg->gid = (unsigned long) sqlite3_column_int64(select, 5); |
40 | 40 | extern int pdb_cancel_unlink_file(pseudo_msg_t *msg); |
41 | 41 | extern int pdb_did_unlink_file(char *path, int deleting); |
42 | 42 | extern int pdb_did_unlink_files(int deleting); |
43 | extern int pdb_link_file(pseudo_msg_t *msg); | |
43 | extern int pdb_link_file(pseudo_msg_t *msg, long long *row); | |
44 | 44 | extern int pdb_may_unlink_file(pseudo_msg_t *msg, int deleting); |
45 | 45 | extern int pdb_unlink_file(pseudo_msg_t *msg); |
46 | 46 | extern int pdb_unlink_file_dev(pseudo_msg_t *msg); |
50 | 50 | extern int pdb_unlink_contents(pseudo_msg_t *msg); |
51 | 51 | extern int pdb_rename_file(const char *oldpath, pseudo_msg_t *msg); |
52 | 52 | extern int pdb_renumber_all(dev_t from, dev_t to); |
53 | extern int pdb_find_file_exact(pseudo_msg_t *msg); | |
54 | extern int pdb_find_file_path(pseudo_msg_t *msg); | |
55 | extern int pdb_find_file_dev(pseudo_msg_t *msg); | |
56 | extern int pdb_find_file_ino(pseudo_msg_t *msg); | |
53 | extern int pdb_find_file_exact(pseudo_msg_t *msg, long long *row); | |
54 | extern int pdb_find_file_path(pseudo_msg_t *msg, long long *row); | |
55 | extern int pdb_find_file_dev(pseudo_msg_t *msg, long long *row); | |
56 | extern int pdb_find_file_ino(pseudo_msg_t *msg, long long *row); | |
57 | 57 | extern char *pdb_get_file_path(pseudo_msg_t *msg); |
58 | extern int pdb_get_xattr(long long file_id, char **value, size_t *len); | |
59 | extern int pdb_list_xattr(long long file_id, char **value, size_t *len); | |
60 | extern int pdb_remove_xattr(long long file_id, char *value, size_t len); | |
61 | extern int pdb_set_xattr(long long file_id, char *value, size_t len, int flags); | |
58 | 62 | |
59 | 63 | struct log_history; |
60 | 64 | typedef struct log_history *log_history; |
0 | 0 | /* |
1 | 1 | * pseudo_server.c, pseudo's server-side logic and message handling |
2 | ||
2 | 3 | * |
3 | 4 | * Copyright (c) 2008-2010, 2013 Wind River Systems, Inc. |
4 | 5 | * |
267 | 268 | in = pseudo_msg_receive(clients[i].fd); |
268 | 269 | if (in) { |
269 | 270 | char *response_path = 0; |
271 | size_t response_pathlen; | |
270 | 272 | int send_response = 1; |
271 | 273 | pseudo_debug(PDBGF_SERVER | PDBGF_VERBOSE, "got a message (%d): %s\n", in->type, (in->pathlen ? in->path : "<no path>")); |
272 | 274 | /* handle incoming ping */ |
305 | 307 | if (in->type != PSEUDO_MSG_SHUTDOWN) { |
306 | 308 | if (in->type == PSEUDO_MSG_FASTOP) |
307 | 309 | send_response = 0; |
308 | if (pseudo_server_response(in, clients[i].program, clients[i].tag)) { | |
310 | /* most messages don't need these, but xattr may */ | |
311 | response_path = 0; | |
312 | response_pathlen = -1; | |
313 | if (pseudo_server_response(in, clients[i].program, clients[i].tag, &response_path, &response_pathlen)) { | |
309 | 314 | in->type = PSEUDO_MSG_NAK; |
310 | 315 | } else { |
311 | 316 | in->type = PSEUDO_MSG_ACK; |
312 | 317 | pseudo_debug(PDBGF_SERVER | PDBGF_VERBOSE, "response: %d (%s)\n", |
313 | 318 | in->result, pseudo_res_name(in->result)); |
314 | 319 | } |
315 | /* no path in response */ | |
316 | in->pathlen = 0; | |
317 | 320 | in->client = i; |
321 | if (response_path) { | |
322 | in->pathlen = response_pathlen; | |
323 | } else { | |
324 | in->pathlen = 0; | |
325 | } | |
318 | 326 | } else { |
319 | 327 | /* the server's listen fd is "a client", and |
320 | 328 | * so is the program connecting to request a shutdown. |
346 | 354 | } |
347 | 355 | } |
348 | 356 | if (send_response) { |
349 | if ((rc = pseudo_msg_send(clients[i].fd, in, -1, response_path)) != 0) { | |
357 | if ((rc = pseudo_msg_send(clients[i].fd, in, in->pathlen, response_path)) != 0) { | |
350 | 358 | pseudo_debug(PDBGF_SERVER, "failed to send response to client %d [%d]: %d (%s)\n", |
351 | 359 | i, (int) clients[i].pid, rc, strerror(errno)); |
352 | 360 | } |
17 | 17 | * |
18 | 18 | */ |
19 | 19 | extern int pseudo_server_start(int); |
20 | extern int pseudo_server_response(pseudo_msg_t *msg, const char *program, const char *tag); | |
20 | extern int pseudo_server_response(pseudo_msg_t *msg, const char *program, const char *tag, char **response_path, size_t *response_len); | |
21 | 21 | extern int pseudo_server_timeout; |
22 | 22 | extern int opt_l; |
418 | 418 | |
419 | 419 | wrote += write(pseudo_util_debug_fd, debuff, len); |
420 | 420 | return wrote; |
421 | } | |
422 | ||
423 | /* given n, pick a multiple of block enough bigger than n | |
424 | * to give us some breathing room. | |
425 | */ | |
426 | static inline size_t | |
427 | round_up(size_t n, size_t block) { | |
428 | return block * (((n + block / 4) / block) + 1); | |
429 | 421 | } |
430 | 422 | |
431 | 423 | /* store pid in text form for prepending to messages */ |
47 | 47 | #include "pseudo_ipc.h" |
48 | 48 | #include "pseudo_client.h" |
49 | 49 | |
50 | /* Types and declarations we need in advance. */ | |
51 | #include "pseudo_wrapper_table.c" | |
52 | ||
50 | 53 | static void pseudo_enosys(const char *); |
51 | 54 | static int pseudo_check_wrappers(void); |
52 | 55 | static volatile int antimagic = 0; |
60 | 63 | |
61 | 64 | extern char *program_invocation_short_name; |
62 | 65 | static sigset_t pseudo_saved_sigmask; |
63 | ||
64 | /* the generated code goes here */ | |
65 | #include "pseudo_wrapper_table.c" | |
66 | #include "pseudo_wrapfuncs.c" | |
67 | 66 | |
68 | 67 | /* Constructor only exists in libpseudo */ |
69 | 68 | static void _libpseudo_init(void) __attribute__ ((constructor)); |
251 | 250 | return _libpseudo_initted; |
252 | 251 | } |
253 | 252 | |
253 | /* the generated code goes here */ | |
254 | 254 | #include "port_wrappers.c" |
255 | #include "pseudo_wrapfuncs.c" | |
256 |