vfs_dir.c revision 8532159f5521ba24e697f0d25970ae89ff62a1f2
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 as published by
11 *  the Free Software Foundation; either version 2 of the License, or
12 *  (at your option) any later version.
13 *
14 *  This program is distributed in the hope that it will be useful,
15 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 *  GNU General Public License for more details.
18 *
19 *  You should have received a copy of the GNU General Public License
20 *  along with this program; if not, write to:
21 *  Free Software Foundation
22 *  51 Franklin Street, Fifth Floor
23 *  Boston, MA  02111-1301  USA
24 *
25 */
26
27#include <linux/module.h>
28#include <linux/errno.h>
29#include <linux/fs.h>
30#include <linux/file.h>
31#include <linux/stat.h>
32#include <linux/string.h>
33#include <linux/smp_lock.h>
34#include <linux/inet.h>
35#include <linux/idr.h>
36
37#include "debug.h"
38#include "v9fs.h"
39#include "9p.h"
40#include "conv.h"
41#include "v9fs_vfs.h"
42#include "fid.h"
43
44/**
45 * dt_type - return file type
46 * @mistat: mistat structure
47 *
48 */
49
50static inline int dt_type(struct v9fs_stat *mistat)
51{
52	unsigned long perm = mistat->mode;
53	int rettype = DT_REG;
54
55	if (perm & V9FS_DMDIR)
56		rettype = DT_DIR;
57	if (perm & V9FS_DMSYMLINK)
58		rettype = DT_LNK;
59
60	return rettype;
61}
62
63/**
64 * v9fs_dir_readdir - read a directory
65 * @filep: opened file structure
66 * @dirent: directory structure ???
67 * @filldir: function to populate directory structure ???
68 *
69 */
70
71static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir)
72{
73	struct v9fs_fcall *fcall = NULL;
74	struct inode *inode = filp->f_dentry->d_inode;
75	struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode);
76	struct v9fs_fid *file = filp->private_data;
77	unsigned int i, n, s;
78	int fid = -1;
79	int ret = 0;
80	struct v9fs_stat stat;
81	int over = 0;
82
83	dprintk(DEBUG_VFS, "name %s\n", filp->f_dentry->d_name.name);
84
85	fid = file->fid;
86
87	if (file->rdir_fcall && (filp->f_pos != file->rdir_pos)) {
88		kfree(file->rdir_fcall);
89		file->rdir_fcall = NULL;
90	}
91
92	if (file->rdir_fcall) {
93		n = file->rdir_fcall->params.rread.count;
94		i = file->rdir_fpos;
95		while (i < n) {
96			s = v9fs_deserialize_stat(
97				file->rdir_fcall->params.rread.data + i,
98				n - i, &stat, v9ses->extended);
99
100			if (s == 0) {
101				dprintk(DEBUG_ERROR,
102					"error while deserializing stat\n");
103				ret = -EIO;
104				goto FreeStructs;
105			}
106
107			over = filldir(dirent, stat.name.str, stat.name.len,
108				    filp->f_pos, v9fs_qid2ino(&stat.qid),
109				    dt_type(&stat));
110
111			if (over) {
112				file->rdir_fpos = i;
113				file->rdir_pos = filp->f_pos;
114				break;
115			}
116
117			i += s;
118			filp->f_pos += s;
119		}
120
121		if (!over) {
122			kfree(file->rdir_fcall);
123			file->rdir_fcall = NULL;
124		}
125	}
126
127	while (!over) {
128		ret = v9fs_t_read(v9ses, fid, filp->f_pos,
129			v9ses->maxdata-V9FS_IOHDRSZ, &fcall);
130		if (ret < 0) {
131			dprintk(DEBUG_ERROR, "error while reading: %d: %p\n",
132				ret, fcall);
133			goto FreeStructs;
134		} else if (ret == 0)
135			break;
136
137		n = ret;
138		i = 0;
139		while (i < n) {
140			s = v9fs_deserialize_stat(fcall->params.rread.data + i,
141				n - i, &stat, v9ses->extended);
142
143			if (s == 0) {
144				dprintk(DEBUG_ERROR,
145					"error while deserializing stat\n");
146				return -EIO;
147			}
148
149			over = filldir(dirent, stat.name.str, stat.name.len,
150				    filp->f_pos, v9fs_qid2ino(&stat.qid),
151				    dt_type(&stat));
152
153			if (over) {
154				file->rdir_fcall = fcall;
155				file->rdir_fpos = i;
156				file->rdir_pos = filp->f_pos;
157				fcall = NULL;
158				break;
159			}
160
161			i += s;
162			filp->f_pos += s;
163		}
164
165		kfree(fcall);
166	}
167
168      FreeStructs:
169	kfree(fcall);
170	return ret;
171}
172
173/**
174 * v9fs_dir_release - close a directory
175 * @inode: inode of the directory
176 * @filp: file pointer to a directory
177 *
178 */
179
180int v9fs_dir_release(struct inode *inode, struct file *filp)
181{
182	struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode);
183	struct v9fs_fid *fid = filp->private_data;
184	int fidnum = -1;
185
186	dprintk(DEBUG_VFS, "inode: %p filp: %p fid: %d\n", inode, filp,
187		fid->fid);
188	fidnum = fid->fid;
189
190	filemap_write_and_wait(inode->i_mapping);
191
192	if (fidnum >= 0) {
193		dprintk(DEBUG_VFS, "fidopen: %d v9f->fid: %d\n", fid->fidopen,
194			fid->fid);
195
196		if (v9fs_t_clunk(v9ses, fidnum))
197			dprintk(DEBUG_ERROR, "clunk failed\n");
198
199		kfree(fid->rdir_fcall);
200		kfree(fid);
201
202		filp->private_data = NULL;
203	}
204
205	return 0;
206}
207
208struct file_operations v9fs_dir_operations = {
209	.read = generic_read_dir,
210	.readdir = v9fs_dir_readdir,
211	.open = v9fs_file_open,
212	.release = v9fs_dir_release,
213};
214