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
36#include "debug.h"
37#include "v9fs.h"
38#include "9p.h"
39#include "conv.h"
40#include "v9fs_vfs.h"
41#include "fid.h"
42
43/**
44 * dt_type - return file type
45 * @mistat: mistat structure
46 *
47 */
48
49static inline int dt_type(struct v9fs_stat *mistat)
50{
51	unsigned long perm = mistat->mode;
52	int rettype = DT_REG;
53
54	if (perm & V9FS_DMDIR)
55		rettype = DT_DIR;
56	if (perm & V9FS_DMSYMLINK)
57		rettype = DT_LNK;
58
59	return rettype;
60}
61
62/**
63 * v9fs_dir_readdir - read a directory
64 * @filep: opened file structure
65 * @dirent: directory structure ???
66 * @filldir: function to populate directory structure ???
67 *
68 */
69
70static int v9fs_dir_readdir(struct file *filp, void *dirent, filldir_t filldir)
71{
72	struct v9fs_fcall *fcall = NULL;
73	struct inode *inode = filp->f_path.dentry->d_inode;
74	struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode);
75	struct v9fs_fid *file = filp->private_data;
76	unsigned int i, n, s;
77	int fid = -1;
78	int ret = 0;
79	struct v9fs_stat stat;
80	int over = 0;
81
82	dprintk(DEBUG_VFS, "name %s\n", filp->f_path.dentry->d_name.name);
83
84	fid = file->fid;
85
86	if (file->rdir_fcall && (filp->f_pos != file->rdir_pos)) {
87		kfree(file->rdir_fcall);
88		file->rdir_fcall = NULL;
89	}
90
91	if (file->rdir_fcall) {
92		n = file->rdir_fcall->params.rread.count;
93		i = file->rdir_fpos;
94		while (i < n) {
95			s = v9fs_deserialize_stat(
96				file->rdir_fcall->params.rread.data + i,
97				n - i, &stat, v9ses->extended);
98
99			if (s == 0) {
100				dprintk(DEBUG_ERROR,
101					"error while deserializing stat\n");
102				ret = -EIO;
103				goto FreeStructs;
104			}
105
106			over = filldir(dirent, stat.name.str, stat.name.len,
107				    filp->f_pos, v9fs_qid2ino(&stat.qid),
108				    dt_type(&stat));
109
110			if (over) {
111				file->rdir_fpos = i;
112				file->rdir_pos = filp->f_pos;
113				break;
114			}
115
116			i += s;
117			filp->f_pos += s;
118		}
119
120		if (!over) {
121			kfree(file->rdir_fcall);
122			file->rdir_fcall = NULL;
123		}
124	}
125
126	while (!over) {
127		ret = v9fs_t_read(v9ses, fid, filp->f_pos,
128			v9ses->maxdata-V9FS_IOHDRSZ, &fcall);
129		if (ret < 0) {
130			dprintk(DEBUG_ERROR, "error while reading: %d: %p\n",
131				ret, fcall);
132			goto FreeStructs;
133		} else if (ret == 0)
134			break;
135
136		n = ret;
137		i = 0;
138		while (i < n) {
139			s = v9fs_deserialize_stat(fcall->params.rread.data + i,
140				n - i, &stat, v9ses->extended);
141
142			if (s == 0) {
143				dprintk(DEBUG_ERROR,
144					"error while deserializing stat\n");
145				return -EIO;
146			}
147
148			over = filldir(dirent, stat.name.str, stat.name.len,
149				    filp->f_pos, v9fs_qid2ino(&stat.qid),
150				    dt_type(&stat));
151
152			if (over) {
153				file->rdir_fcall = fcall;
154				file->rdir_fpos = i;
155				file->rdir_pos = filp->f_pos;
156				fcall = NULL;
157				break;
158			}
159
160			i += s;
161			filp->f_pos += s;
162		}
163
164		kfree(fcall);
165	}
166
167      FreeStructs:
168	kfree(fcall);
169	return ret;
170}
171
172/**
173 * v9fs_dir_release - close a directory
174 * @inode: inode of the directory
175 * @filp: file pointer to a directory
176 *
177 */
178
179int v9fs_dir_release(struct inode *inode, struct file *filp)
180{
181	struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode);
182	struct v9fs_fid *fid = filp->private_data;
183	int fidnum = -1;
184
185	dprintk(DEBUG_VFS, "inode: %p filp: %p fid: %d\n", inode, filp,
186		fid->fid);
187	fidnum = fid->fid;
188
189	filemap_write_and_wait(inode->i_mapping);
190
191	if (fidnum >= 0) {
192		dprintk(DEBUG_VFS, "fidopen: %d v9f->fid: %d\n", fid->fidopen,
193			fid->fid);
194
195		if (v9fs_t_clunk(v9ses, fidnum))
196			dprintk(DEBUG_ERROR, "clunk failed\n");
197
198		kfree(fid->rdir_fcall);
199		kfree(fid);
200
201		filp->private_data = NULL;
202	}
203
204	return 0;
205}
206
207const struct file_operations v9fs_dir_operations = {
208	.read = generic_read_dir,
209	.readdir = v9fs_dir_readdir,
210	.open = v9fs_file_open,
211	.release = v9fs_dir_release,
212};
213