diff options
author | Khairul Rohaizzat Jamaluddin <khairul.rohaizzat.jamaluddin@intel.com> | 2021-04-07 05:42:35 +0000 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2021-04-18 11:37:24 +0100 |
commit | b50e51b1c848e0162e693e986be59c15e5e57e5c (patch) | |
tree | 480fa560f39d79da4dcfa0c510c8d00686c3b6b6 | |
parent | 84a6d97670d6ec0589b2591b4a660b3b069e21cb (diff) | |
download | poky-b50e51b1c848e0162e693e986be59c15e5e57e5c.tar.gz |
qemu: Fix CVE-2020-35517
CVE:
CVE-2020-35517
(From OE-Core rev: 51376edb13eed748395ebe1e56081c092565be9b)
Signed-off-by: Khairul Rohaizzat Jamaluddin <khairul.rohaizzat.jamaluddin@intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r-- | meta/recipes-devtools/qemu/qemu.inc | 3 | ||||
-rw-r--r-- | meta/recipes-devtools/qemu/qemu/CVE-2020-35517_1.patch | 153 | ||||
-rw-r--r-- | meta/recipes-devtools/qemu/qemu/CVE-2020-35517_2.patch | 117 | ||||
-rw-r--r-- | meta/recipes-devtools/qemu/qemu/CVE-2020-35517_3.patch | 303 |
4 files changed, 576 insertions, 0 deletions
diff --git a/meta/recipes-devtools/qemu/qemu.inc b/meta/recipes-devtools/qemu/qemu.inc index a625809597..f0416b0379 100644 --- a/meta/recipes-devtools/qemu/qemu.inc +++ b/meta/recipes-devtools/qemu/qemu.inc | |||
@@ -31,6 +31,9 @@ SRC_URI = "https://download.qemu.org/${BPN}-${PV}.tar.xz \ | |||
31 | file://determinism.patch \ | 31 | file://determinism.patch \ |
32 | file://0001-tests-meson.build-use-relative-path-to-refer-to-file.patch \ | 32 | file://0001-tests-meson.build-use-relative-path-to-refer-to-file.patch \ |
33 | file://CVE-2021-20203.patch \ | 33 | file://CVE-2021-20203.patch \ |
34 | file://CVE-2020-35517_1.patch \ | ||
35 | file://CVE-2020-35517_2.patch \ | ||
36 | file://CVE-2020-35517_3.patch \ | ||
34 | " | 37 | " |
35 | UPSTREAM_CHECK_REGEX = "qemu-(?P<pver>\d+(\.\d+)+)\.tar" | 38 | UPSTREAM_CHECK_REGEX = "qemu-(?P<pver>\d+(\.\d+)+)\.tar" |
36 | 39 | ||
diff --git a/meta/recipes-devtools/qemu/qemu/CVE-2020-35517_1.patch b/meta/recipes-devtools/qemu/qemu/CVE-2020-35517_1.patch new file mode 100644 index 0000000000..73a4cb2064 --- /dev/null +++ b/meta/recipes-devtools/qemu/qemu/CVE-2020-35517_1.patch | |||
@@ -0,0 +1,153 @@ | |||
1 | From 8afaaee976965b7fb90ec225a51d60f35c5f173c Mon Sep 17 00:00:00 2001 | ||
2 | From: Stefan Hajnoczi <stefanha@redhat.com> | ||
3 | Date: Thu, 4 Feb 2021 15:02:06 +0000 | ||
4 | Subject: [PATCH] virtiofsd: extract lo_do_open() from lo_open() | ||
5 | |||
6 | Both lo_open() and lo_create() have similar code to open a file. Extract | ||
7 | a common lo_do_open() function from lo_open() that will be used by | ||
8 | lo_create() in a later commit. | ||
9 | |||
10 | Since lo_do_open() does not otherwise need fuse_req_t req, convert | ||
11 | lo_add_fd_mapping() to use struct lo_data *lo instead. | ||
12 | |||
13 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
14 | Message-Id: <20210204150208.367837-2-stefanha@redhat.com> | ||
15 | Reviewed-by: Greg Kurz <groug@kaod.org> | ||
16 | Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com> | ||
17 | |||
18 | Upstream-Status: Backport | ||
19 | [https://github.com/qemu/qemu/commit/8afaaee976965b7fb90ec225a51d60f35c5f173c] | ||
20 | |||
21 | CVE: CVE-2020-35517 | ||
22 | |||
23 | Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com> | ||
24 | Signed-off-by: Khairul Rohaizzat Jamaluddin <khairul.rohaizzat.jamaluddin@intel.com> | ||
25 | --- | ||
26 | tools/virtiofsd/passthrough_ll.c | 73 +++++++++++++++++++++++++--------------- | ||
27 | 1 file changed, 46 insertions(+), 27 deletions(-) | ||
28 | |||
29 | diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c | ||
30 | index 5fb36d9..f14fa51 100644 | ||
31 | --- a/tools/virtiofsd/passthrough_ll.c | ||
32 | +++ b/tools/virtiofsd/passthrough_ll.c | ||
33 | @@ -459,17 +459,17 @@ static void lo_map_remove(struct lo_map *map, size_t key) | ||
34 | } | ||
35 | |||
36 | /* Assumes lo->mutex is held */ | ||
37 | -static ssize_t lo_add_fd_mapping(fuse_req_t req, int fd) | ||
38 | +static ssize_t lo_add_fd_mapping(struct lo_data *lo, int fd) | ||
39 | { | ||
40 | struct lo_map_elem *elem; | ||
41 | |||
42 | - elem = lo_map_alloc_elem(&lo_data(req)->fd_map); | ||
43 | + elem = lo_map_alloc_elem(&lo->fd_map); | ||
44 | if (!elem) { | ||
45 | return -1; | ||
46 | } | ||
47 | |||
48 | elem->fd = fd; | ||
49 | - return elem - lo_data(req)->fd_map.elems; | ||
50 | + return elem - lo->fd_map.elems; | ||
51 | } | ||
52 | |||
53 | /* Assumes lo->mutex is held */ | ||
54 | @@ -1651,6 +1651,38 @@ static void update_open_flags(int writeback, int allow_direct_io, | ||
55 | } | ||
56 | } | ||
57 | |||
58 | +static int lo_do_open(struct lo_data *lo, struct lo_inode *inode, | ||
59 | + struct fuse_file_info *fi) | ||
60 | +{ | ||
61 | + char buf[64]; | ||
62 | + ssize_t fh; | ||
63 | + int fd; | ||
64 | + | ||
65 | + update_open_flags(lo->writeback, lo->allow_direct_io, fi); | ||
66 | + | ||
67 | + sprintf(buf, "%i", inode->fd); | ||
68 | + fd = openat(lo->proc_self_fd, buf, fi->flags & ~O_NOFOLLOW); | ||
69 | + if (fd == -1) { | ||
70 | + return errno; | ||
71 | + } | ||
72 | + | ||
73 | + pthread_mutex_lock(&lo->mutex); | ||
74 | + fh = lo_add_fd_mapping(lo, fd); | ||
75 | + pthread_mutex_unlock(&lo->mutex); | ||
76 | + if (fh == -1) { | ||
77 | + close(fd); | ||
78 | + return ENOMEM; | ||
79 | + } | ||
80 | + | ||
81 | + fi->fh = fh; | ||
82 | + if (lo->cache == CACHE_NONE) { | ||
83 | + fi->direct_io = 1; | ||
84 | + } else if (lo->cache == CACHE_ALWAYS) { | ||
85 | + fi->keep_cache = 1; | ||
86 | + } | ||
87 | + return 0; | ||
88 | +} | ||
89 | + | ||
90 | static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, | ||
91 | mode_t mode, struct fuse_file_info *fi) | ||
92 | { | ||
93 | @@ -1691,7 +1723,7 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, | ||
94 | ssize_t fh; | ||
95 | |||
96 | pthread_mutex_lock(&lo->mutex); | ||
97 | - fh = lo_add_fd_mapping(req, fd); | ||
98 | + fh = lo_add_fd_mapping(lo, fd); | ||
99 | pthread_mutex_unlock(&lo->mutex); | ||
100 | if (fh == -1) { | ||
101 | close(fd); | ||
102 | @@ -1892,38 +1924,25 @@ static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync, | ||
103 | |||
104 | static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) | ||
105 | { | ||
106 | - int fd; | ||
107 | - ssize_t fh; | ||
108 | - char buf[64]; | ||
109 | struct lo_data *lo = lo_data(req); | ||
110 | + struct lo_inode *inode = lo_inode(req, ino); | ||
111 | + int err; | ||
112 | |||
113 | fuse_log(FUSE_LOG_DEBUG, "lo_open(ino=%" PRIu64 ", flags=%d)\n", ino, | ||
114 | fi->flags); | ||
115 | |||
116 | - update_open_flags(lo->writeback, lo->allow_direct_io, fi); | ||
117 | - | ||
118 | - sprintf(buf, "%i", lo_fd(req, ino)); | ||
119 | - fd = openat(lo->proc_self_fd, buf, fi->flags & ~O_NOFOLLOW); | ||
120 | - if (fd == -1) { | ||
121 | - return (void)fuse_reply_err(req, errno); | ||
122 | - } | ||
123 | - | ||
124 | - pthread_mutex_lock(&lo->mutex); | ||
125 | - fh = lo_add_fd_mapping(req, fd); | ||
126 | - pthread_mutex_unlock(&lo->mutex); | ||
127 | - if (fh == -1) { | ||
128 | - close(fd); | ||
129 | - fuse_reply_err(req, ENOMEM); | ||
130 | + if (!inode) { | ||
131 | + fuse_reply_err(req, EBADF); | ||
132 | return; | ||
133 | } | ||
134 | |||
135 | - fi->fh = fh; | ||
136 | - if (lo->cache == CACHE_NONE) { | ||
137 | - fi->direct_io = 1; | ||
138 | - } else if (lo->cache == CACHE_ALWAYS) { | ||
139 | - fi->keep_cache = 1; | ||
140 | + err = lo_do_open(lo, inode, fi); | ||
141 | + lo_inode_put(lo, &inode); | ||
142 | + if (err) { | ||
143 | + fuse_reply_err(req, err); | ||
144 | + } else { | ||
145 | + fuse_reply_open(req, fi); | ||
146 | } | ||
147 | - fuse_reply_open(req, fi); | ||
148 | } | ||
149 | |||
150 | static void lo_release(fuse_req_t req, fuse_ino_t ino, | ||
151 | -- | ||
152 | 1.8.3.1 | ||
153 | |||
diff --git a/meta/recipes-devtools/qemu/qemu/CVE-2020-35517_2.patch b/meta/recipes-devtools/qemu/qemu/CVE-2020-35517_2.patch new file mode 100644 index 0000000000..bf11bdb6f8 --- /dev/null +++ b/meta/recipes-devtools/qemu/qemu/CVE-2020-35517_2.patch | |||
@@ -0,0 +1,117 @@ | |||
1 | From 22d2ece71e533310da31f2857ebc4a00d91968b3 Mon Sep 17 00:00:00 2001 | ||
2 | From: Stefan Hajnoczi <stefanha@redhat.com> | ||
3 | Date: Thu, 4 Feb 2021 15:02:07 +0000 | ||
4 | Subject: [PATCH] virtiofsd: optionally return inode pointer from | ||
5 | lo_do_lookup() | ||
6 | |||
7 | lo_do_lookup() finds an existing inode or allocates a new one. It | ||
8 | increments nlookup so that the inode stays alive until the client | ||
9 | releases it. | ||
10 | |||
11 | Existing callers don't need the struct lo_inode so the function doesn't | ||
12 | return it. Extend the function to optionally return the inode. The next | ||
13 | commit will need it. | ||
14 | |||
15 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
16 | Reviewed-by: Greg Kurz <groug@kaod.org> | ||
17 | Message-Id: <20210204150208.367837-3-stefanha@redhat.com> | ||
18 | Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com> | ||
19 | |||
20 | Upstream-Status: Backport | ||
21 | [https://github.com/qemu/qemu/commit/22d2ece71e533310da31f2857ebc4a00d91968b3] | ||
22 | |||
23 | CVE: CVE-2020-35517 | ||
24 | |||
25 | Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com> | ||
26 | Signed-off-by: Khairul Rohaizzat Jamaluddin <khairul.rohaizzat.jamaluddin@intel.com> | ||
27 | --- | ||
28 | tools/virtiofsd/passthrough_ll.c | 29 +++++++++++++++++++++-------- | ||
29 | 1 file changed, 21 insertions(+), 8 deletions(-) | ||
30 | |||
31 | diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c | ||
32 | index f14fa51..aa35fc6 100644 | ||
33 | --- a/tools/virtiofsd/passthrough_ll.c | ||
34 | +++ b/tools/virtiofsd/passthrough_ll.c | ||
35 | @@ -831,11 +831,13 @@ static int do_statx(struct lo_data *lo, int dirfd, const char *pathname, | ||
36 | } | ||
37 | |||
38 | /* | ||
39 | - * Increments nlookup and caller must release refcount using | ||
40 | - * lo_inode_put(&parent). | ||
41 | + * Increments nlookup on the inode on success. unref_inode_lolocked() must be | ||
42 | + * called eventually to decrement nlookup again. If inodep is non-NULL, the | ||
43 | + * inode pointer is stored and the caller must call lo_inode_put(). | ||
44 | */ | ||
45 | static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, | ||
46 | - struct fuse_entry_param *e) | ||
47 | + struct fuse_entry_param *e, | ||
48 | + struct lo_inode **inodep) | ||
49 | { | ||
50 | int newfd; | ||
51 | int res; | ||
52 | @@ -845,6 +847,10 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, | ||
53 | struct lo_inode *inode = NULL; | ||
54 | struct lo_inode *dir = lo_inode(req, parent); | ||
55 | |||
56 | + if (inodep) { | ||
57 | + *inodep = NULL; | ||
58 | + } | ||
59 | + | ||
60 | /* | ||
61 | * name_to_handle_at() and open_by_handle_at() can reach here with fuse | ||
62 | * mount point in guest, but we don't have its inode info in the | ||
63 | @@ -913,7 +919,14 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, | ||
64 | pthread_mutex_unlock(&lo->mutex); | ||
65 | } | ||
66 | e->ino = inode->fuse_ino; | ||
67 | - lo_inode_put(lo, &inode); | ||
68 | + | ||
69 | + /* Transfer ownership of inode pointer to caller or drop it */ | ||
70 | + if (inodep) { | ||
71 | + *inodep = inode; | ||
72 | + } else { | ||
73 | + lo_inode_put(lo, &inode); | ||
74 | + } | ||
75 | + | ||
76 | lo_inode_put(lo, &dir); | ||
77 | |||
78 | fuse_log(FUSE_LOG_DEBUG, " %lli/%s -> %lli\n", (unsigned long long)parent, | ||
79 | @@ -948,7 +961,7 @@ static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) | ||
80 | return; | ||
81 | } | ||
82 | |||
83 | - err = lo_do_lookup(req, parent, name, &e); | ||
84 | + err = lo_do_lookup(req, parent, name, &e, NULL); | ||
85 | if (err) { | ||
86 | fuse_reply_err(req, err); | ||
87 | } else { | ||
88 | @@ -1056,7 +1069,7 @@ static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent, | ||
89 | goto out; | ||
90 | } | ||
91 | |||
92 | - saverr = lo_do_lookup(req, parent, name, &e); | ||
93 | + saverr = lo_do_lookup(req, parent, name, &e, NULL); | ||
94 | if (saverr) { | ||
95 | goto out; | ||
96 | } | ||
97 | @@ -1534,7 +1547,7 @@ static void lo_do_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, | ||
98 | |||
99 | if (plus) { | ||
100 | if (!is_dot_or_dotdot(name)) { | ||
101 | - err = lo_do_lookup(req, ino, name, &e); | ||
102 | + err = lo_do_lookup(req, ino, name, &e, NULL); | ||
103 | if (err) { | ||
104 | goto error; | ||
105 | } | ||
106 | @@ -1732,7 +1745,7 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, | ||
107 | } | ||
108 | |||
109 | fi->fh = fh; | ||
110 | - err = lo_do_lookup(req, parent, name, &e); | ||
111 | + err = lo_do_lookup(req, parent, name, &e, NULL); | ||
112 | } | ||
113 | if (lo->cache == CACHE_NONE) { | ||
114 | fi->direct_io = 1; | ||
115 | -- | ||
116 | 1.8.3.1 | ||
117 | |||
diff --git a/meta/recipes-devtools/qemu/qemu/CVE-2020-35517_3.patch b/meta/recipes-devtools/qemu/qemu/CVE-2020-35517_3.patch new file mode 100644 index 0000000000..f348f3f2bd --- /dev/null +++ b/meta/recipes-devtools/qemu/qemu/CVE-2020-35517_3.patch | |||
@@ -0,0 +1,303 @@ | |||
1 | From a3fdbbc7f271bff7d53d0501b29d910ece0b3789 Mon Sep 17 00:00:00 2001 | ||
2 | From: Stefan Hajnoczi <stefanha@redhat.com> | ||
3 | Date: Thu, 4 Feb 2021 15:02:08 +0000 | ||
4 | Subject: [PATCH] virtiofsd: prevent opening of special files (CVE-2020-35517) | ||
5 | |||
6 | A well-behaved FUSE client does not attempt to open special files with | ||
7 | FUSE_OPEN because they are handled on the client side (e.g. device nodes | ||
8 | are handled by client-side device drivers). | ||
9 | |||
10 | The check to prevent virtiofsd from opening special files is missing in | ||
11 | a few cases, most notably FUSE_OPEN. A malicious client can cause | ||
12 | virtiofsd to open a device node, potentially allowing the guest to | ||
13 | escape. This can be exploited by a modified guest device driver. It is | ||
14 | not exploitable from guest userspace since the guest kernel will handle | ||
15 | special files inside the guest instead of sending FUSE requests. | ||
16 | |||
17 | This patch fixes this issue by introducing the lo_inode_open() function | ||
18 | to check the file type before opening it. This is a short-term solution | ||
19 | because it does not prevent a compromised virtiofsd process from opening | ||
20 | device nodes on the host. | ||
21 | |||
22 | Restructure lo_create() to try O_CREAT | O_EXCL first. Note that O_CREAT | ||
23 | | O_EXCL does not follow symlinks, so O_NOFOLLOW masking is not | ||
24 | necessary here. If the file exists and the user did not specify O_EXCL, | ||
25 | open it via lo_do_open(). | ||
26 | |||
27 | Reported-by: Alex Xu <alex@alxu.ca> | ||
28 | Fixes: CVE-2020-35517 | ||
29 | Reviewed-by: Dr. David Alan Gilbert <dgilbert@redhat.com> | ||
30 | Reviewed-by: Vivek Goyal <vgoyal@redhat.com> | ||
31 | Reviewed-by: Greg Kurz <groug@kaod.org> | ||
32 | Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> | ||
33 | Message-Id: <20210204150208.367837-4-stefanha@redhat.com> | ||
34 | Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com> | ||
35 | |||
36 | Upstream-Status: Backport | ||
37 | [https://github.com/qemu/qemu/commit/a3fdbbc7f271bff7d53d0501b29d910ece0b3789] | ||
38 | |||
39 | CVE: CVE-2020-35517 | ||
40 | |||
41 | Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com> | ||
42 | Signed-off-by: Khairul Rohaizzat Jamaluddin <khairul.rohaizzat.jamaluddin@intel.com> | ||
43 | --- | ||
44 | tools/virtiofsd/passthrough_ll.c | 144 ++++++++++++++++++++----------- | ||
45 | 1 file changed, 92 insertions(+), 52 deletions(-) | ||
46 | |||
47 | diff --git a/tools/virtiofsd/passthrough_ll.c b/tools/virtiofsd/passthrough_ll.c | ||
48 | index aa35fc6ba5a5..147b59338a18 100644 | ||
49 | --- a/tools/virtiofsd/passthrough_ll.c | ||
50 | +++ b/tools/virtiofsd/passthrough_ll.c | ||
51 | @@ -555,6 +555,38 @@ static int lo_fd(fuse_req_t req, fuse_ino_t ino) | ||
52 | return fd; | ||
53 | } | ||
54 | |||
55 | +/* | ||
56 | + * Open a file descriptor for an inode. Returns -EBADF if the inode is not a | ||
57 | + * regular file or a directory. | ||
58 | + * | ||
59 | + * Use this helper function instead of raw openat(2) to prevent security issues | ||
60 | + * when a malicious client opens special files such as block device nodes. | ||
61 | + * Symlink inodes are also rejected since symlinks must already have been | ||
62 | + * traversed on the client side. | ||
63 | + */ | ||
64 | +static int lo_inode_open(struct lo_data *lo, struct lo_inode *inode, | ||
65 | + int open_flags) | ||
66 | +{ | ||
67 | + g_autofree char *fd_str = g_strdup_printf("%d", inode->fd); | ||
68 | + int fd; | ||
69 | + | ||
70 | + if (!S_ISREG(inode->filetype) && !S_ISDIR(inode->filetype)) { | ||
71 | + return -EBADF; | ||
72 | + } | ||
73 | + | ||
74 | + /* | ||
75 | + * The file is a symlink so O_NOFOLLOW must be ignored. We checked earlier | ||
76 | + * that the inode is not a special file but if an external process races | ||
77 | + * with us then symlinks are traversed here. It is not possible to escape | ||
78 | + * the shared directory since it is mounted as "/" though. | ||
79 | + */ | ||
80 | + fd = openat(lo->proc_self_fd, fd_str, open_flags & ~O_NOFOLLOW); | ||
81 | + if (fd < 0) { | ||
82 | + return -errno; | ||
83 | + } | ||
84 | + return fd; | ||
85 | +} | ||
86 | + | ||
87 | static void lo_init(void *userdata, struct fuse_conn_info *conn) | ||
88 | { | ||
89 | struct lo_data *lo = (struct lo_data *)userdata; | ||
90 | @@ -684,9 +716,9 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, | ||
91 | if (fi) { | ||
92 | truncfd = fd; | ||
93 | } else { | ||
94 | - sprintf(procname, "%i", ifd); | ||
95 | - truncfd = openat(lo->proc_self_fd, procname, O_RDWR); | ||
96 | + truncfd = lo_inode_open(lo, inode, O_RDWR); | ||
97 | if (truncfd < 0) { | ||
98 | + errno = -truncfd; | ||
99 | goto out_err; | ||
100 | } | ||
101 | } | ||
102 | @@ -848,7 +880,7 @@ static int lo_do_lookup(fuse_req_t req, fuse_ino_t parent, const char *name, | ||
103 | struct lo_inode *dir = lo_inode(req, parent); | ||
104 | |||
105 | if (inodep) { | ||
106 | - *inodep = NULL; | ||
107 | + *inodep = NULL; /* in case there is an error */ | ||
108 | } | ||
109 | |||
110 | /* | ||
111 | @@ -1664,19 +1696,26 @@ static void update_open_flags(int writeback, int allow_direct_io, | ||
112 | } | ||
113 | } | ||
114 | |||
115 | +/* | ||
116 | + * Open a regular file, set up an fd mapping, and fill out the struct | ||
117 | + * fuse_file_info for it. If existing_fd is not negative, use that fd instead | ||
118 | + * opening a new one. Takes ownership of existing_fd. | ||
119 | + * | ||
120 | + * Returns 0 on success or a positive errno. | ||
121 | + */ | ||
122 | static int lo_do_open(struct lo_data *lo, struct lo_inode *inode, | ||
123 | - struct fuse_file_info *fi) | ||
124 | + int existing_fd, struct fuse_file_info *fi) | ||
125 | { | ||
126 | - char buf[64]; | ||
127 | ssize_t fh; | ||
128 | - int fd; | ||
129 | + int fd = existing_fd; | ||
130 | |||
131 | update_open_flags(lo->writeback, lo->allow_direct_io, fi); | ||
132 | |||
133 | - sprintf(buf, "%i", inode->fd); | ||
134 | - fd = openat(lo->proc_self_fd, buf, fi->flags & ~O_NOFOLLOW); | ||
135 | - if (fd == -1) { | ||
136 | - return errno; | ||
137 | + if (fd < 0) { | ||
138 | + fd = lo_inode_open(lo, inode, fi->flags); | ||
139 | + if (fd < 0) { | ||
140 | + return -fd; | ||
141 | + } | ||
142 | } | ||
143 | |||
144 | pthread_mutex_lock(&lo->mutex); | ||
145 | @@ -1699,9 +1738,10 @@ static int lo_do_open(struct lo_data *lo, struct lo_inode *inode, | ||
146 | static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, | ||
147 | mode_t mode, struct fuse_file_info *fi) | ||
148 | { | ||
149 | - int fd; | ||
150 | + int fd = -1; | ||
151 | struct lo_data *lo = lo_data(req); | ||
152 | struct lo_inode *parent_inode; | ||
153 | + struct lo_inode *inode = NULL; | ||
154 | struct fuse_entry_param e; | ||
155 | int err; | ||
156 | struct lo_cred old = {}; | ||
157 | @@ -1727,36 +1767,38 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name, | ||
158 | |||
159 | update_open_flags(lo->writeback, lo->allow_direct_io, fi); | ||
160 | |||
161 | - fd = openat(parent_inode->fd, name, (fi->flags | O_CREAT) & ~O_NOFOLLOW, | ||
162 | - mode); | ||
163 | + /* Try to create a new file but don't open existing files */ | ||
164 | + fd = openat(parent_inode->fd, name, fi->flags | O_CREAT | O_EXCL, mode); | ||
165 | err = fd == -1 ? errno : 0; | ||
166 | - lo_restore_cred(&old); | ||
167 | |||
168 | - if (!err) { | ||
169 | - ssize_t fh; | ||
170 | + lo_restore_cred(&old); | ||
171 | |||
172 | - pthread_mutex_lock(&lo->mutex); | ||
173 | - fh = lo_add_fd_mapping(lo, fd); | ||
174 | - pthread_mutex_unlock(&lo->mutex); | ||
175 | - if (fh == -1) { | ||
176 | - close(fd); | ||
177 | - err = ENOMEM; | ||
178 | - goto out; | ||
179 | - } | ||
180 | + /* Ignore the error if file exists and O_EXCL was not given */ | ||
181 | + if (err && (err != EEXIST || (fi->flags & O_EXCL))) { | ||
182 | + goto out; | ||
183 | + } | ||
184 | |||
185 | - fi->fh = fh; | ||
186 | - err = lo_do_lookup(req, parent, name, &e, NULL); | ||
187 | + err = lo_do_lookup(req, parent, name, &e, &inode); | ||
188 | + if (err) { | ||
189 | + goto out; | ||
190 | } | ||
191 | - if (lo->cache == CACHE_NONE) { | ||
192 | - fi->direct_io = 1; | ||
193 | - } else if (lo->cache == CACHE_ALWAYS) { | ||
194 | - fi->keep_cache = 1; | ||
195 | + | ||
196 | + err = lo_do_open(lo, inode, fd, fi); | ||
197 | + fd = -1; /* lo_do_open() takes ownership of fd */ | ||
198 | + if (err) { | ||
199 | + /* Undo lo_do_lookup() nlookup ref */ | ||
200 | + unref_inode_lolocked(lo, inode, 1); | ||
201 | } | ||
202 | |||
203 | out: | ||
204 | + lo_inode_put(lo, &inode); | ||
205 | lo_inode_put(lo, &parent_inode); | ||
206 | |||
207 | if (err) { | ||
208 | + if (fd >= 0) { | ||
209 | + close(fd); | ||
210 | + } | ||
211 | + | ||
212 | fuse_reply_err(req, err); | ||
213 | } else { | ||
214 | fuse_reply_create(req, &e, fi); | ||
215 | @@ -1770,7 +1812,6 @@ static struct lo_inode_plock *lookup_create_plock_ctx(struct lo_data *lo, | ||
216 | pid_t pid, int *err) | ||
217 | { | ||
218 | struct lo_inode_plock *plock; | ||
219 | - char procname[64]; | ||
220 | int fd; | ||
221 | |||
222 | plock = | ||
223 | @@ -1787,12 +1828,10 @@ static struct lo_inode_plock *lookup_create_plock_ctx(struct lo_data *lo, | ||
224 | } | ||
225 | |||
226 | /* Open another instance of file which can be used for ofd locks. */ | ||
227 | - sprintf(procname, "%i", inode->fd); | ||
228 | - | ||
229 | /* TODO: What if file is not writable? */ | ||
230 | - fd = openat(lo->proc_self_fd, procname, O_RDWR); | ||
231 | - if (fd == -1) { | ||
232 | - *err = errno; | ||
233 | + fd = lo_inode_open(lo, inode, O_RDWR); | ||
234 | + if (fd < 0) { | ||
235 | + *err = -fd; | ||
236 | free(plock); | ||
237 | return NULL; | ||
238 | } | ||
239 | @@ -1949,7 +1988,7 @@ static void lo_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) | ||
240 | return; | ||
241 | } | ||
242 | |||
243 | - err = lo_do_open(lo, inode, fi); | ||
244 | + err = lo_do_open(lo, inode, -1, fi); | ||
245 | lo_inode_put(lo, &inode); | ||
246 | if (err) { | ||
247 | fuse_reply_err(req, err); | ||
248 | @@ -2014,39 +2053,40 @@ static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) | ||
249 | static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, | ||
250 | struct fuse_file_info *fi) | ||
251 | { | ||
252 | + struct lo_inode *inode = lo_inode(req, ino); | ||
253 | + struct lo_data *lo = lo_data(req); | ||
254 | int res; | ||
255 | int fd; | ||
256 | - char *buf; | ||
257 | |||
258 | fuse_log(FUSE_LOG_DEBUG, "lo_fsync(ino=%" PRIu64 ", fi=0x%p)\n", ino, | ||
259 | (void *)fi); | ||
260 | |||
261 | - if (!fi) { | ||
262 | - struct lo_data *lo = lo_data(req); | ||
263 | - | ||
264 | - res = asprintf(&buf, "%i", lo_fd(req, ino)); | ||
265 | - if (res == -1) { | ||
266 | - return (void)fuse_reply_err(req, errno); | ||
267 | - } | ||
268 | + if (!inode) { | ||
269 | + fuse_reply_err(req, EBADF); | ||
270 | + return; | ||
271 | + } | ||
272 | |||
273 | - fd = openat(lo->proc_self_fd, buf, O_RDWR); | ||
274 | - free(buf); | ||
275 | - if (fd == -1) { | ||
276 | - return (void)fuse_reply_err(req, errno); | ||
277 | + if (!fi) { | ||
278 | + fd = lo_inode_open(lo, inode, O_RDWR); | ||
279 | + if (fd < 0) { | ||
280 | + res = -fd; | ||
281 | + goto out; | ||
282 | } | ||
283 | } else { | ||
284 | fd = lo_fi_fd(req, fi); | ||
285 | } | ||
286 | |||
287 | if (datasync) { | ||
288 | - res = fdatasync(fd); | ||
289 | + res = fdatasync(fd) == -1 ? errno : 0; | ||
290 | } else { | ||
291 | - res = fsync(fd); | ||
292 | + res = fsync(fd) == -1 ? errno : 0; | ||
293 | } | ||
294 | if (!fi) { | ||
295 | close(fd); | ||
296 | } | ||
297 | - fuse_reply_err(req, res == -1 ? errno : 0); | ||
298 | +out: | ||
299 | + lo_inode_put(lo, &inode); | ||
300 | + fuse_reply_err(req, res); | ||
301 | } | ||
302 | |||
303 | static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t offset, | ||