Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1836d959 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ericvh/v9fs:
  9p: fix readdir corner cases
  9p: fix readlink
  9p: fix a small bug in readdir for long directories
parents 333a0743 3e2796a9
Loading
Loading
Loading
Loading
+67 −26
Original line number Diff line number Diff line
@@ -39,6 +39,24 @@
#include "v9fs_vfs.h"
#include "fid.h"

/**
 * struct p9_rdir - readdir accounting
 * @mutex: mutex protecting readdir
 * @head: start offset of current dirread buffer
 * @tail: end offset of current dirread buffer
 * @buf: dirread buffer
 *
 * private structure for keeping track of readdir
 * allocated on demand
 */

struct p9_rdir {
	struct mutex mutex;
	int head;
	int tail;
	uint8_t *buf;
};

/**
 * dt_type - return file type
 * @mistat: mistat structure
@@ -70,56 +88,79 @@ static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir)
{
	int over;
	struct p9_wstat st;
	int err;
	int err = 0;
	struct p9_fid *fid;
	int buflen;
	char *statbuf;
	int n, i = 0;
	int reclen = 0;
	struct p9_rdir *rdir;

	P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name);
	fid = filp->private_data;

	buflen = fid->clnt->msize - P9_IOHDRSZ;
	statbuf = kmalloc(buflen, GFP_KERNEL);
	if (!statbuf)
		return -ENOMEM;

	while (1) {
		err = v9fs_file_readn(filp, statbuf, NULL, buflen,
								fid->rdir_fpos);
	/* allocate rdir on demand */
	if (!fid->rdir) {
		rdir = kmalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL);

		if (rdir == NULL) {
			err = -ENOMEM;
			goto exit;
		}
		spin_lock(&filp->f_dentry->d_lock);
		if (!fid->rdir) {
			rdir->buf = (uint8_t *)rdir + sizeof(struct p9_rdir);
			mutex_init(&rdir->mutex);
			rdir->head = rdir->tail = 0;
			fid->rdir = (void *) rdir;
			rdir = NULL;
		}
		spin_unlock(&filp->f_dentry->d_lock);
		kfree(rdir);
	}
	rdir = (struct p9_rdir *) fid->rdir;

	err = mutex_lock_interruptible(&rdir->mutex);
	while (err == 0) {
		if (rdir->tail == rdir->head) {
			err = v9fs_file_readn(filp, rdir->buf, NULL,
							buflen, filp->f_pos);
			if (err <= 0)
			break;
				goto unlock_and_exit;

		n = err;
		while (i < n) {
			err = p9stat_read(statbuf + i, buflen-i, &st,
			rdir->head = 0;
			rdir->tail = err;
		}

		while (rdir->head < rdir->tail) {
			err = p9stat_read(rdir->buf + rdir->head,
						buflen - rdir->head, &st,
						fid->clnt->dotu);
			if (err) {
				P9_DPRINTK(P9_DEBUG_VFS, "returned %d\n", err);
				err = -EIO;
				p9stat_free(&st);
				goto free_and_exit;
				goto unlock_and_exit;
			}

			i += st.size+2;
			fid->rdir_fpos += st.size+2;
			reclen = st.size+2;

			over = filldir(dirent, st.name, strlen(st.name),
			    filp->f_pos, v9fs_qid2ino(&st.qid), dt_type(&st));

			filp->f_pos += st.size+2;

			p9stat_free(&st);

			if (over) {
				err = 0;
				goto free_and_exit;
				goto unlock_and_exit;
			}
			rdir->head += reclen;
			filp->f_pos += reclen;
		}
	}

free_and_exit:
	kfree(statbuf);
unlock_and_exit:
	mutex_unlock(&rdir->mutex);
exit:
	return err;
}

+2 −3
Original line number Diff line number Diff line
@@ -994,8 +994,7 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen)
	P9_DPRINTK(P9_DEBUG_VFS,
		"%s -> %s (%s)\n", dentry->d_name.name, st->extension, buffer);

	retval = buflen;

	retval = strnlen(buffer, buflen);
done:
	kfree(st);
	return retval;
@@ -1062,7 +1061,7 @@ static void *v9fs_vfs_follow_link(struct dentry *dentry, struct nameidata *nd)
			__putname(link);
			link = ERR_PTR(len);
		} else
			link[len] = 0;
			link[min(len, PATH_MAX-1)] = 0;
	}
	nd_set_link(nd, link);

+3 −4
Original line number Diff line number Diff line
@@ -159,8 +159,7 @@ struct p9_client {
 * @qid: the &p9_qid server identifier this handle points to
 * @iounit: the server reported maximum transaction size for this file
 * @uid: the numeric uid of the local user who owns this handle
 * @aux: transport specific information (unused?)
 * @rdir_fpos: tracks offset of file position when reading directory contents
 * @rdir: readdir accounting structure (allocated on demand)
 * @flist: per-client-instance fid tracking
 * @dlist: per-dentry fid tracking
 *
@@ -174,9 +173,9 @@ struct p9_fid {
	struct p9_qid qid;
	u32 iounit;
	uid_t uid;
	void *aux;

	int rdir_fpos;
	void *rdir;

	struct list_head flist;
	struct list_head dlist;	/* list of all fids attached to a dentry */
};
+2 −3
Original line number Diff line number Diff line
@@ -582,11 +582,9 @@ static struct p9_fid *p9_fid_create(struct p9_client *clnt)

	memset(&fid->qid, 0, sizeof(struct p9_qid));
	fid->mode = -1;
	fid->rdir_fpos = 0;
	fid->uid = current_fsuid();
	fid->clnt = clnt;
	fid->aux = NULL;

	fid->rdir = NULL;
	spin_lock_irqsave(&clnt->lock, flags);
	list_add(&fid->flist, &clnt->fidlist);
	spin_unlock_irqrestore(&clnt->lock, flags);
@@ -609,6 +607,7 @@ static void p9_fid_destroy(struct p9_fid *fid)
	spin_lock_irqsave(&clnt->lock, flags);
	list_del(&fid->flist);
	spin_unlock_irqrestore(&clnt->lock, flags);
	kfree(fid->rdir);
	kfree(fid);
}