mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/
synced 2025-04-19 20:58:31 +09:00
9p update for 6.15-rc1
- fix handling of bogus (negative/too long) replies - fix crash on mkdir with ACLs (... looks like nobody is using ACLs with semi-recent kernels...) - ipv6 support for trans=tcp - minor concurrency fix to make syzbot happy - minor cleanup -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE/IPbcYBuWt0zoYhOq06b7GqY5nAFAmfuAoIACgkQq06b7GqY 5nBb9w/+K9WnU4MdSTFSXDJ+ZZTY//fPpFaUTqHl1hTeRjmIBtBdngy9ASvnPrPj n6DHnd+qkdFV6cMvs5wPUskRxJZuRDugzZMAd6yzjJoRNPmNFN2Ux7EXWEdFwvFG mk4EJtzgiZhp7XWlNzQeMziuDmMZJijzLsd4zVYNo9fNKEh5jLKjKWyHTVRxfuCc i22Y8oUgcghK0YSSLoL59xF4nRrvn57DBF3wnrW6pqVvVQ05NJRH4fNgXp4wW497 jxQq01ela7IgNUoMgib7F0ov1fu8pSEd95T+fzcqynZCePQ9rzDbvt3MR7rjJuqo /VXwW7N3KT6DrQG6Wu21B9VcfBeWjdbtJ/GWGVp8d2iP04Sv0escx53qETZSD0iZ pMIZLthJuXlq9dmxZ/j+BPLlbm7uAFPbP15/O9Un5xVvrisANFm1TPvM77btnrEP KovWfooheoUrK6DmkKbkzS5HJH2ko4CASAG7c8GL+R1hXwVDswC06cecyvXaKQQK Um4nOe59hRqbqWXmIEs4jssoUjfg8MfuX71DvX0p6+r1WR+eySieG2HiTz/mTj0q /27cCWlAvjYxa42opxASAD1/HvW2tZfcPKtSQbh/3s0FBpTVqbof3fxmnTjcb0Po V7WpuRSD7DnmawjbQQLXznUQokagO23/ySO1vARnluKyGwsn5yI= =Q0mE -----END PGP SIGNATURE----- Merge tag '9p-for-6.15-rc1' of https://github.com/martinetd/linux Pull 9p updates from Dominique Martinet: - fix handling of bogus (negative/too long) replies - fix crash on mkdir with ACLs (... looks like nobody is using ACLs with semi-recent kernels...) - ipv6 support for trans=tcp - minor concurrency fix to make syzbot happy - minor cleanup * tag '9p-for-6.15-rc1' of https://github.com/martinetd/linux: docs: fs/9p: Add missing "not" in cache documentation 9p: Use hashtable.h for hash_errmap Documentation/fs/9p: fix broken link 9p/trans_fd: mark concurrent read and writes to p9_conn->err 9p/net: return error on bogus (longer than requested) replies 9p/net: fix improper handling of bogus negative read/write replies fs/9p: fix NULL pointer dereference on mkdir net/9p/fd: support ipv6 for trans=tcp
This commit is contained in:
commit
bdafff62ae
@ -40,7 +40,7 @@ For remote file server::
|
||||
|
||||
mount -t 9p 10.10.1.2 /mnt/9
|
||||
|
||||
For Plan 9 From User Space applications (http://swtch.com/plan9)::
|
||||
For Plan 9 From User Space applications (https://9fans.github.io/plan9port/)::
|
||||
|
||||
mount -t 9p `namespace`/acme /mnt/9 -o trans=unix,uname=$USER
|
||||
|
||||
@ -165,8 +165,8 @@ Options
|
||||
do not necessarily validate cached values on the server. In other
|
||||
words changes on the server are not guaranteed to be reflected
|
||||
on the client system. Only use this mode of operation if you
|
||||
have an exclusive mount and the server will modify the filesystem
|
||||
underneath you.
|
||||
have an exclusive mount and the server will not modify the
|
||||
filesystem underneath you.
|
||||
|
||||
debug=n specifies debug level. The debug level is a bitmask.
|
||||
|
||||
|
@ -407,8 +407,8 @@ static struct dentry *v9fs_vfs_mkdir_dotl(struct mnt_idmap *idmap,
|
||||
err);
|
||||
goto error;
|
||||
}
|
||||
v9fs_fid_add(dentry, &fid);
|
||||
v9fs_set_create_acl(inode, fid, dacl, pacl);
|
||||
v9fs_fid_add(dentry, &fid);
|
||||
d_instantiate(dentry, inode);
|
||||
err = 0;
|
||||
inc_nlink(dir);
|
||||
|
@ -1548,7 +1548,8 @@ p9_client_read_once(struct p9_fid *fid, u64 offset, struct iov_iter *to,
|
||||
struct p9_client *clnt = fid->clnt;
|
||||
struct p9_req_t *req;
|
||||
int count = iov_iter_count(to);
|
||||
int rsize, received, non_zc = 0;
|
||||
u32 rsize, received;
|
||||
bool non_zc = false;
|
||||
char *dataptr;
|
||||
|
||||
*err = 0;
|
||||
@ -1571,7 +1572,7 @@ p9_client_read_once(struct p9_fid *fid, u64 offset, struct iov_iter *to,
|
||||
0, 11, "dqd", fid->fid,
|
||||
offset, rsize);
|
||||
} else {
|
||||
non_zc = 1;
|
||||
non_zc = true;
|
||||
req = p9_client_rpc(clnt, P9_TREAD, "dqd", fid->fid, offset,
|
||||
rsize);
|
||||
}
|
||||
@ -1592,11 +1593,13 @@ p9_client_read_once(struct p9_fid *fid, u64 offset, struct iov_iter *to,
|
||||
return 0;
|
||||
}
|
||||
if (rsize < received) {
|
||||
pr_err("bogus RREAD count (%d > %d)\n", received, rsize);
|
||||
received = rsize;
|
||||
pr_err("bogus RREAD count (%u > %u)\n", received, rsize);
|
||||
*err = -EIO;
|
||||
p9_req_put(clnt, req);
|
||||
return 0;
|
||||
}
|
||||
|
||||
p9_debug(P9_DEBUG_9P, "<<< RREAD count %d\n", received);
|
||||
p9_debug(P9_DEBUG_9P, "<<< RREAD count %u\n", received);
|
||||
|
||||
if (non_zc) {
|
||||
int n = copy_to_iter(dataptr, received, to);
|
||||
@ -1623,9 +1626,9 @@ p9_client_write(struct p9_fid *fid, u64 offset, struct iov_iter *from, int *err)
|
||||
*err = 0;
|
||||
|
||||
while (iov_iter_count(from)) {
|
||||
int count = iov_iter_count(from);
|
||||
int rsize = fid->iounit;
|
||||
int written;
|
||||
size_t count = iov_iter_count(from);
|
||||
u32 rsize = fid->iounit;
|
||||
u32 written;
|
||||
|
||||
if (!rsize || rsize > clnt->msize - P9_IOHDRSZ)
|
||||
rsize = clnt->msize - P9_IOHDRSZ;
|
||||
@ -1633,7 +1636,7 @@ p9_client_write(struct p9_fid *fid, u64 offset, struct iov_iter *from, int *err)
|
||||
if (count < rsize)
|
||||
rsize = count;
|
||||
|
||||
p9_debug(P9_DEBUG_9P, ">>> TWRITE fid %d offset %llu count %d (/%d)\n",
|
||||
p9_debug(P9_DEBUG_9P, ">>> TWRITE fid %d offset %llu count %u (/%zu)\n",
|
||||
fid->fid, offset, rsize, count);
|
||||
|
||||
/* Don't bother zerocopy for small IO (< 1024) */
|
||||
@ -1659,11 +1662,14 @@ p9_client_write(struct p9_fid *fid, u64 offset, struct iov_iter *from, int *err)
|
||||
break;
|
||||
}
|
||||
if (rsize < written) {
|
||||
pr_err("bogus RWRITE count (%d > %d)\n", written, rsize);
|
||||
written = rsize;
|
||||
pr_err("bogus RWRITE count (%u > %u)\n", written, rsize);
|
||||
*err = -EIO;
|
||||
iov_iter_revert(from, count - iov_iter_count(from));
|
||||
p9_req_put(clnt, req);
|
||||
break;
|
||||
}
|
||||
|
||||
p9_debug(P9_DEBUG_9P, "<<< RWRITE count %d\n", written);
|
||||
p9_debug(P9_DEBUG_9P, "<<< RWRITE count %u\n", written);
|
||||
|
||||
p9_req_put(clnt, req);
|
||||
iov_iter_revert(from, count - written - iov_iter_count(from));
|
||||
@ -1712,7 +1718,7 @@ p9_client_write_subreq(struct netfs_io_subrequest *subreq)
|
||||
|
||||
if (written > len) {
|
||||
pr_err("bogus RWRITE count (%d > %u)\n", written, len);
|
||||
written = len;
|
||||
written = -EIO;
|
||||
}
|
||||
|
||||
p9_debug(P9_DEBUG_9P, "<<< RWRITE count %d\n", len);
|
||||
@ -2098,7 +2104,8 @@ EXPORT_SYMBOL_GPL(p9_client_xattrcreate);
|
||||
|
||||
int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset)
|
||||
{
|
||||
int err, rsize, non_zc = 0;
|
||||
int err, non_zc = 0;
|
||||
u32 rsize;
|
||||
struct p9_client *clnt;
|
||||
struct p9_req_t *req;
|
||||
char *dataptr;
|
||||
@ -2107,7 +2114,7 @@ int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset)
|
||||
|
||||
iov_iter_kvec(&to, ITER_DEST, &kv, 1, count);
|
||||
|
||||
p9_debug(P9_DEBUG_9P, ">>> TREADDIR fid %d offset %llu count %d\n",
|
||||
p9_debug(P9_DEBUG_9P, ">>> TREADDIR fid %d offset %llu count %u\n",
|
||||
fid->fid, offset, count);
|
||||
|
||||
clnt = fid->clnt;
|
||||
@ -2142,11 +2149,12 @@ int p9_client_readdir(struct p9_fid *fid, char *data, u32 count, u64 offset)
|
||||
goto free_and_error;
|
||||
}
|
||||
if (rsize < count) {
|
||||
pr_err("bogus RREADDIR count (%d > %d)\n", count, rsize);
|
||||
count = rsize;
|
||||
pr_err("bogus RREADDIR count (%u > %u)\n", count, rsize);
|
||||
err = -EIO;
|
||||
goto free_and_error;
|
||||
}
|
||||
|
||||
p9_debug(P9_DEBUG_9P, "<<< RREADDIR count %d\n", count);
|
||||
p9_debug(P9_DEBUG_9P, "<<< RREADDIR count %u\n", count);
|
||||
|
||||
if (non_zc)
|
||||
memmove(data, dataptr, count);
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/jhash.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/hashtable.h>
|
||||
#include <net/9p/9p.h>
|
||||
|
||||
/**
|
||||
@ -33,8 +34,8 @@ struct errormap {
|
||||
struct hlist_node list;
|
||||
};
|
||||
|
||||
#define ERRHASHSZ 32
|
||||
static struct hlist_head hash_errmap[ERRHASHSZ];
|
||||
#define ERRHASH_BITS 5
|
||||
static DEFINE_HASHTABLE(hash_errmap, ERRHASH_BITS);
|
||||
|
||||
/* FixMe - reduce to a reasonable size */
|
||||
static struct errormap errmap[] = {
|
||||
@ -176,18 +177,14 @@ static struct errormap errmap[] = {
|
||||
int p9_error_init(void)
|
||||
{
|
||||
struct errormap *c;
|
||||
int bucket;
|
||||
|
||||
/* initialize hash table */
|
||||
for (bucket = 0; bucket < ERRHASHSZ; bucket++)
|
||||
INIT_HLIST_HEAD(&hash_errmap[bucket]);
|
||||
u32 hash;
|
||||
|
||||
/* load initial error map into hash table */
|
||||
for (c = errmap; c->name; c++) {
|
||||
c->namelen = strlen(c->name);
|
||||
bucket = jhash(c->name, c->namelen, 0) % ERRHASHSZ;
|
||||
hash = jhash(c->name, c->namelen, 0);
|
||||
INIT_HLIST_NODE(&c->list);
|
||||
hlist_add_head(&c->list, &hash_errmap[bucket]);
|
||||
hash_add(hash_errmap, &c->list, hash);
|
||||
}
|
||||
|
||||
return 1;
|
||||
@ -205,12 +202,12 @@ int p9_errstr2errno(char *errstr, int len)
|
||||
{
|
||||
int errno;
|
||||
struct errormap *c;
|
||||
int bucket;
|
||||
u32 hash;
|
||||
|
||||
errno = 0;
|
||||
c = NULL;
|
||||
bucket = jhash(errstr, len, 0) % ERRHASHSZ;
|
||||
hlist_for_each_entry(c, &hash_errmap[bucket], list) {
|
||||
hash = jhash(errstr, len, 0);
|
||||
hash_for_each_possible(hash_errmap, c, list, hash) {
|
||||
if (c->namelen == len && !memcmp(c->name, errstr, len)) {
|
||||
errno = c->val;
|
||||
break;
|
||||
|
@ -11,6 +11,7 @@
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/in.h>
|
||||
#include <linux/in6.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/ipv6.h>
|
||||
@ -191,12 +192,13 @@ static void p9_conn_cancel(struct p9_conn *m, int err)
|
||||
|
||||
spin_lock(&m->req_lock);
|
||||
|
||||
if (m->err) {
|
||||
if (READ_ONCE(m->err)) {
|
||||
spin_unlock(&m->req_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
m->err = err;
|
||||
WRITE_ONCE(m->err, err);
|
||||
ASSERT_EXCLUSIVE_WRITER(m->err);
|
||||
|
||||
list_for_each_entry_safe(req, rtmp, &m->req_list, req_list) {
|
||||
list_move(&req->req_list, &cancel_list);
|
||||
@ -283,7 +285,7 @@ static void p9_read_work(struct work_struct *work)
|
||||
|
||||
m = container_of(work, struct p9_conn, rq);
|
||||
|
||||
if (m->err < 0)
|
||||
if (READ_ONCE(m->err) < 0)
|
||||
return;
|
||||
|
||||
p9_debug(P9_DEBUG_TRANS, "start mux %p pos %zd\n", m, m->rc.offset);
|
||||
@ -450,7 +452,7 @@ static void p9_write_work(struct work_struct *work)
|
||||
|
||||
m = container_of(work, struct p9_conn, wq);
|
||||
|
||||
if (m->err < 0) {
|
||||
if (READ_ONCE(m->err) < 0) {
|
||||
clear_bit(Wworksched, &m->wsched);
|
||||
return;
|
||||
}
|
||||
@ -622,7 +624,7 @@ static void p9_poll_mux(struct p9_conn *m)
|
||||
__poll_t n;
|
||||
int err = -ECONNRESET;
|
||||
|
||||
if (m->err < 0)
|
||||
if (READ_ONCE(m->err) < 0)
|
||||
return;
|
||||
|
||||
n = p9_fd_poll(m->client, NULL, &err);
|
||||
@ -665,6 +667,7 @@ static void p9_poll_mux(struct p9_conn *m)
|
||||
static int p9_fd_request(struct p9_client *client, struct p9_req_t *req)
|
||||
{
|
||||
__poll_t n;
|
||||
int err;
|
||||
struct p9_trans_fd *ts = client->trans;
|
||||
struct p9_conn *m = &ts->conn;
|
||||
|
||||
@ -673,9 +676,10 @@ static int p9_fd_request(struct p9_client *client, struct p9_req_t *req)
|
||||
|
||||
spin_lock(&m->req_lock);
|
||||
|
||||
if (m->err < 0) {
|
||||
err = READ_ONCE(m->err);
|
||||
if (err < 0) {
|
||||
spin_unlock(&m->req_lock);
|
||||
return m->err;
|
||||
return err;
|
||||
}
|
||||
|
||||
WRITE_ONCE(req->status, REQ_STATUS_UNSENT);
|
||||
@ -954,64 +958,55 @@ static void p9_fd_close(struct p9_client *client)
|
||||
kfree(ts);
|
||||
}
|
||||
|
||||
/*
|
||||
* stolen from NFS - maybe should be made a generic function?
|
||||
*/
|
||||
static inline int valid_ipaddr4(const char *buf)
|
||||
{
|
||||
int rc, count, in[4];
|
||||
|
||||
rc = sscanf(buf, "%d.%d.%d.%d", &in[0], &in[1], &in[2], &in[3]);
|
||||
if (rc != 4)
|
||||
return -EINVAL;
|
||||
for (count = 0; count < 4; count++) {
|
||||
if (in[count] > 255)
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int p9_bind_privport(struct socket *sock)
|
||||
{
|
||||
struct sockaddr_in cl;
|
||||
struct sockaddr_storage stor = { 0 };
|
||||
int port, err = -EINVAL;
|
||||
|
||||
memset(&cl, 0, sizeof(cl));
|
||||
cl.sin_family = AF_INET;
|
||||
cl.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
stor.ss_family = sock->ops->family;
|
||||
if (stor.ss_family == AF_INET)
|
||||
((struct sockaddr_in *)&stor)->sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
else
|
||||
((struct sockaddr_in6 *)&stor)->sin6_addr = in6addr_any;
|
||||
for (port = p9_ipport_resv_max; port >= p9_ipport_resv_min; port--) {
|
||||
cl.sin_port = htons((ushort)port);
|
||||
err = kernel_bind(sock, (struct sockaddr *)&cl, sizeof(cl));
|
||||
if (stor.ss_family == AF_INET)
|
||||
((struct sockaddr_in *)&stor)->sin_port = htons((ushort)port);
|
||||
else
|
||||
((struct sockaddr_in6 *)&stor)->sin6_port = htons((ushort)port);
|
||||
err = kernel_bind(sock, (struct sockaddr *)&stor, sizeof(stor));
|
||||
if (err != -EADDRINUSE)
|
||||
break;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
p9_fd_create_tcp(struct p9_client *client, const char *addr, char *args)
|
||||
{
|
||||
int err;
|
||||
char port_str[6];
|
||||
struct socket *csocket;
|
||||
struct sockaddr_in sin_server;
|
||||
struct sockaddr_storage stor = { 0 };
|
||||
struct p9_fd_opts opts;
|
||||
|
||||
err = parse_opts(args, &opts);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (addr == NULL || valid_ipaddr4(addr) < 0)
|
||||
if (!addr)
|
||||
return -EINVAL;
|
||||
|
||||
sprintf(port_str, "%u", opts.port);
|
||||
err = inet_pton_with_scope(current->nsproxy->net_ns, AF_UNSPEC, addr,
|
||||
port_str, &stor);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
csocket = NULL;
|
||||
|
||||
client->trans_opts.tcp.port = opts.port;
|
||||
client->trans_opts.tcp.privport = opts.privport;
|
||||
sin_server.sin_family = AF_INET;
|
||||
sin_server.sin_addr.s_addr = in_aton(addr);
|
||||
sin_server.sin_port = htons(opts.port);
|
||||
err = __sock_create(current->nsproxy->net_ns, PF_INET,
|
||||
err = __sock_create(current->nsproxy->net_ns, stor.ss_family,
|
||||
SOCK_STREAM, IPPROTO_TCP, &csocket, 1);
|
||||
if (err) {
|
||||
pr_err("%s (%d): problem creating socket\n",
|
||||
@ -1030,8 +1025,8 @@ p9_fd_create_tcp(struct p9_client *client, const char *addr, char *args)
|
||||
}
|
||||
|
||||
err = READ_ONCE(csocket->ops)->connect(csocket,
|
||||
(struct sockaddr *)&sin_server,
|
||||
sizeof(struct sockaddr_in), 0);
|
||||
(struct sockaddr *)&stor,
|
||||
sizeof(stor), 0);
|
||||
if (err < 0) {
|
||||
pr_err("%s (%d): problem connecting socket to %s\n",
|
||||
__func__, task_pid_nr(current), addr);
|
||||
|
Loading…
x
Reference in New Issue
Block a user