vfs_dir.c revision 5a0e3ad6af8660be21ca98a971cd00f331318c05
1/*
2 * linux/fs/9p/vfs_dir.c
3 *
4 * This file contains vfs directory ops for the 9P2000 protocol.
5 *
6 *  Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
7 *  Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
8 *
9 *  This program is free software; you can redistribute it and/or modify
10 *  it under the terms of the GNU General Public License version 2
11 *  as published by the Free Software Foundation.
12 *
13 *  This program is distributed in the hope that it will be useful,
14 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *  GNU General Public License for more details.
17 *
18 *  You should have received a copy of the GNU General Public License
19 *  along with this program; if not, write to:
20 *  Free Software Foundation
21 *  51 Franklin Street, Fifth Floor
22 *  Boston, MA  02111-1301  USA
23 *
24 */
25
26#include <linux/module.h>
27#include <linux/errno.h>
28#include <linux/fs.h>
29#include <linux/file.h>
30#include <linux/stat.h>
31#include <linux/string.h>
32#include <linux/sched.h>
33#include <linux/inet.h>
34#include <linux/idr.h>
35#include <linux/slab.h>
36#include <net/9p/9p.h>
37#include <net/9p/client.h>
38
39#include "v9fs.h"
40#include "v9fs_vfs.h"
41#include "fid.h"
42
43/**
44 * struct p9_rdir - readdir accounting
45 * @mutex: mutex protecting readdir
46 * @head: start offset of current dirread buffer
47 * @tail: end offset of current dirread buffer
48 * @buf: dirread buffer
49 *
50 * private structure for keeping track of readdir
51 * allocated on demand
52 */
53
54struct p9_rdir {
55	struct mutex mutex;
56	int head;
57	int tail;
58	uint8_t *buf;
59};
60
61/**
62 * dt_type - return file type
63 * @mistat: mistat structure
64 *
65 */
66
67static inline int dt_type(struct p9_wstat *mistat)
68{
69	unsigned long perm = mistat->mode;
70	int rettype = DT_REG;
71
72	if (perm & P9_DMDIR)
73		rettype = DT_DIR;
74	if (perm & P9_DMSYMLINK)
75		rettype = DT_LNK;
76
77	return rettype;
78}
79
80static void p9stat_init(struct p9_wstat *stbuf)
81{
82	stbuf->name  = NULL;
83	stbuf->uid   = NULL;
84	stbuf->gid   = NULL;
85	stbuf->muid  = NULL;
86	stbuf->extension = NULL;
87}
88
89/**
90 * v9fs_dir_readdir - read a directory
91 * @filp: opened file structure
92 * @dirent: directory structure ???
93 * @filldir: function to populate directory structure ???
94 *
95 */
96
97static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir)
98{
99	int over;
100	struct p9_wstat st;
101	int err = 0;
102	struct p9_fid *fid;
103	int buflen;
104	int reclen = 0;
105	struct p9_rdir *rdir;
106
107	P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name);
108	fid = filp->private_data;
109
110	buflen = fid->clnt->msize - P9_IOHDRSZ;
111
112	/* allocate rdir on demand */
113	if (!fid->rdir) {
114		rdir = kmalloc(sizeof(struct p9_rdir) + buflen, GFP_KERNEL);
115
116		if (rdir == NULL) {
117			err = -ENOMEM;
118			goto exit;
119		}
120		spin_lock(&filp->f_dentry->d_lock);
121		if (!fid->rdir) {
122			rdir->buf = (uint8_t *)rdir + sizeof(struct p9_rdir);
123			mutex_init(&rdir->mutex);
124			rdir->head = rdir->tail = 0;
125			fid->rdir = (void *) rdir;
126			rdir = NULL;
127		}
128		spin_unlock(&filp->f_dentry->d_lock);
129		kfree(rdir);
130	}
131	rdir = (struct p9_rdir *) fid->rdir;
132
133	err = mutex_lock_interruptible(&rdir->mutex);
134	while (err == 0) {
135		if (rdir->tail == rdir->head) {
136			err = v9fs_file_readn(filp, rdir->buf, NULL,
137							buflen, filp->f_pos);
138			if (err <= 0)
139				goto unlock_and_exit;
140
141			rdir->head = 0;
142			rdir->tail = err;
143		}
144		while (rdir->head < rdir->tail) {
145			p9stat_init(&st);
146			err = p9stat_read(rdir->buf + rdir->head,
147						buflen - rdir->head, &st,
148						fid->clnt->proto_version);
149			if (err) {
150				P9_DPRINTK(P9_DEBUG_VFS, "returned %d\n", err);
151				err = -EIO;
152				p9stat_free(&st);
153				goto unlock_and_exit;
154			}
155			reclen = st.size+2;
156
157			over = filldir(dirent, st.name, strlen(st.name),
158			    filp->f_pos, v9fs_qid2ino(&st.qid), dt_type(&st));
159
160			p9stat_free(&st);
161
162			if (over) {
163				err = 0;
164				goto unlock_and_exit;
165			}
166			rdir->head += reclen;
167			filp->f_pos += reclen;
168		}
169	}
170
171unlock_and_exit:
172	mutex_unlock(&rdir->mutex);
173exit:
174	return err;
175}
176
177
178/**
179 * v9fs_dir_release - close a directory
180 * @inode: inode of the directory
181 * @filp: file pointer to a directory
182 *
183 */
184
185int v9fs_dir_release(struct inode *inode, struct file *filp)
186{
187	struct p9_fid *fid;
188
189	fid = filp->private_data;
190	P9_DPRINTK(P9_DEBUG_VFS,
191			"inode: %p filp: %p fid: %d\n", inode, filp, fid->fid);
192	filemap_write_and_wait(inode->i_mapping);
193	p9_client_clunk(fid);
194	return 0;
195}
196
197const struct file_operations v9fs_dir_operations = {
198	.read = generic_read_dir,
199	.llseek = generic_file_llseek,
200	.readdir = v9fs_dir_readdir,
201	.open = v9fs_file_open,
202	.release = v9fs_dir_release,
203};
204