Commit bdafff62 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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
parents 5916a6fb 4210030d
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -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.

+1 −1
Original line number Diff line number Diff line
@@ -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);
+26 −18
Original line number Diff line number Diff line
@@ -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);
+9 −12
Original line number Diff line number Diff line
@@ -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;
+34 −39
Original line number Diff line number Diff line
@@ -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);