aboutsummaryrefslogtreecommitdiffstats
path: root/fs/9p
diff options
context:
space:
mode:
Diffstat (limited to 'fs/9p')
-rw-r--r--fs/9p/9p.c359
-rw-r--r--fs/9p/9p.h341
-rw-r--r--fs/9p/Makefile17
-rw-r--r--fs/9p/conv.c693
-rw-r--r--fs/9p/conv.h36
-rw-r--r--fs/9p/debug.h70
-rw-r--r--fs/9p/error.c93
-rw-r--r--fs/9p/error.h178
-rw-r--r--fs/9p/fid.c241
-rw-r--r--fs/9p/fid.h57
-rw-r--r--fs/9p/mux.c475
-rw-r--r--fs/9p/mux.h41
-rw-r--r--fs/9p/trans_fd.c172
-rw-r--r--fs/9p/trans_sock.c290
-rw-r--r--fs/9p/transport.h46
-rw-r--r--fs/9p/v9fs.c452
-rw-r--r--fs/9p/v9fs.h103
-rw-r--r--fs/9p/v9fs_vfs.h53
-rw-r--r--fs/9p/vfs_dentry.c126
-rw-r--r--fs/9p/vfs_dir.c226
-rw-r--r--fs/9p/vfs_file.c401
-rw-r--r--fs/9p/vfs_inode.c1338
-rw-r--r--fs/9p/vfs_super.c280
23 files changed, 6088 insertions, 0 deletions
diff --git a/fs/9p/9p.c b/fs/9p/9p.c
new file mode 100644
index 0000000000..e847f504a4
--- /dev/null
+++ b/fs/9p/9p.c
@@ -0,0 +1,359 @@
1/*
2 * linux/fs/9p/9p.c
3 *
4 * This file contains functions 9P2000 functions
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/config.h>
28#include <linux/module.h>
29#include <linux/errno.h>
30#include <linux/fs.h>
31#include <linux/idr.h>
32
33#include "debug.h"
34#include "v9fs.h"
35#include "9p.h"
36#include "mux.h"
37
38/**
39 * v9fs_t_version - negotiate protocol parameters with sever
40 * @v9ses: 9P2000 session information
41 * @msize: requested max size packet
42 * @version: requested version.extension string
43 * @fcall: pointer to response fcall pointer
44 *
45 */
46
47int
48v9fs_t_version(struct v9fs_session_info *v9ses, u32 msize,
49 char *version, struct v9fs_fcall **fcall)
50{
51 struct v9fs_fcall msg;
52
53 dprintk(DEBUG_9P, "msize: %d version: %s\n", msize, version);
54 msg.id = TVERSION;
55 msg.params.tversion.msize = msize;
56 msg.params.tversion.version = version;
57
58 return v9fs_mux_rpc(v9ses, &msg, fcall);
59}
60
61/**
62 * v9fs_t_attach - mount the server
63 * @v9ses: 9P2000 session information
64 * @uname: user name doing the attach
65 * @aname: remote name being attached to
66 * @fid: mount fid to attatch to root node
67 * @afid: authentication fid (in this case result key)
68 * @fcall: pointer to response fcall pointer
69 *
70 */
71
72int
73v9fs_t_attach(struct v9fs_session_info *v9ses, char *uname, char *aname,
74 u32 fid, u32 afid, struct v9fs_fcall **fcall)
75{
76 struct v9fs_fcall msg;
77
78 dprintk(DEBUG_9P, "uname '%s' aname '%s' fid %d afid %d\n", uname,
79 aname, fid, afid);
80 msg.id = TATTACH;
81 msg.params.tattach.fid = fid;
82 msg.params.tattach.afid = afid;
83 msg.params.tattach.uname = uname;
84 msg.params.tattach.aname = aname;
85
86 return v9fs_mux_rpc(v9ses, &msg, fcall);
87}
88
89/**
90 * v9fs_t_clunk - release a fid (finish a transaction)
91 * @v9ses: 9P2000 session information
92 * @fid: fid to release
93 * @fcall: pointer to response fcall pointer
94 *
95 */
96
97int
98v9fs_t_clunk(struct v9fs_session_info *v9ses, u32 fid,
99 struct v9fs_fcall **fcall)
100{
101 struct v9fs_fcall msg;
102
103 dprintk(DEBUG_9P, "fid %d\n", fid);
104 msg.id = TCLUNK;
105 msg.params.tclunk.fid = fid;
106
107 return v9fs_mux_rpc(v9ses, &msg, fcall);
108}
109
110/**
111 * v9fs_v9fs_t_flush - flush a pending transaction
112 * @v9ses: 9P2000 session information
113 * @tag: tid to release
114 *
115 */
116
117int v9fs_t_flush(struct v9fs_session_info *v9ses, u16 tag)
118{
119 struct v9fs_fcall msg;
120
121 dprintk(DEBUG_9P, "oldtag %d\n", tag);
122 msg.id = TFLUSH;
123 msg.params.tflush.oldtag = tag;
124 return v9fs_mux_rpc(v9ses, &msg, NULL);
125}
126
127/**
128 * v9fs_t_stat - read a file's meta-data
129 * @v9ses: 9P2000 session information
130 * @fid: fid pointing to file or directory to get info about
131 * @fcall: pointer to response fcall
132 *
133 */
134
135int
136v9fs_t_stat(struct v9fs_session_info *v9ses, u32 fid, struct v9fs_fcall **fcall)
137{
138 struct v9fs_fcall msg;
139
140 dprintk(DEBUG_9P, "fid %d\n", fid);
141 if (fcall)
142 *fcall = NULL;
143
144 msg.id = TSTAT;
145 msg.params.tstat.fid = fid;
146 return v9fs_mux_rpc(v9ses, &msg, fcall);
147}
148
149/**
150 * v9fs_t_wstat - write a file's meta-data
151 * @v9ses: 9P2000 session information
152 * @fid: fid pointing to file or directory to write info about
153 * @stat: metadata
154 * @fcall: pointer to response fcall
155 *
156 */
157
158int
159v9fs_t_wstat(struct v9fs_session_info *v9ses, u32 fid,
160 struct v9fs_stat *stat, struct v9fs_fcall **fcall)
161{
162 struct v9fs_fcall msg;
163
164 dprintk(DEBUG_9P, "fid %d length %d\n", fid, (int)stat->length);
165 msg.id = TWSTAT;
166 msg.params.twstat.fid = fid;
167 msg.params.twstat.stat = stat;
168
169 return v9fs_mux_rpc(v9ses, &msg, fcall);
170}
171
172/**
173 * v9fs_t_walk - walk a fid to a new file or directory
174 * @v9ses: 9P2000 session information
175 * @fid: fid to walk
176 * @newfid: new fid (for clone operations)
177 * @name: path to walk fid to
178 * @fcall: pointer to response fcall
179 *
180 */
181
182/* TODO: support multiple walk */
183
184int
185v9fs_t_walk(struct v9fs_session_info *v9ses, u32 fid, u32 newfid,
186 char *name, struct v9fs_fcall **fcall)
187{
188 struct v9fs_fcall msg;
189
190 dprintk(DEBUG_9P, "fid %d newfid %d wname '%s'\n", fid, newfid, name);
191 msg.id = TWALK;
192 msg.params.twalk.fid = fid;
193 msg.params.twalk.newfid = newfid;
194
195 if (name) {
196 msg.params.twalk.nwname = 1;
197 msg.params.twalk.wnames = &name;
198 } else {
199 msg.params.twalk.nwname = 0;
200 }
201
202 return v9fs_mux_rpc(v9ses, &msg, fcall);
203}
204
205/**
206 * v9fs_t_open - open a file
207 *
208 * @v9ses - 9P2000 session information
209 * @fid - fid to open
210 * @mode - mode to open file (R, RW, etc)
211 * @fcall - pointer to response fcall
212 *
213 */
214
215int
216v9fs_t_open(struct v9fs_session_info *v9ses, u32 fid, u8 mode,
217 struct v9fs_fcall **fcall)
218{
219 struct v9fs_fcall msg;
220 long errorno = -1;
221
222 dprintk(DEBUG_9P, "fid %d mode %d\n", fid, mode);
223 msg.id = TOPEN;
224 msg.params.topen.fid = fid;
225 msg.params.topen.mode = mode;
226
227 errorno = v9fs_mux_rpc(v9ses, &msg, fcall);
228
229 return errorno;
230}
231
232/**
233 * v9fs_t_remove - remove a file or directory
234 * @v9ses: 9P2000 session information
235 * @fid: fid to remove
236 * @fcall: pointer to response fcall
237 *
238 */
239
240int
241v9fs_t_remove(struct v9fs_session_info *v9ses, u32 fid,
242 struct v9fs_fcall **fcall)
243{
244 struct v9fs_fcall msg;
245
246 dprintk(DEBUG_9P, "fid %d\n", fid);
247 msg.id = TREMOVE;
248 msg.params.tremove.fid = fid;
249 return v9fs_mux_rpc(v9ses, &msg, fcall);
250}
251
252/**
253 * v9fs_t_create - create a file or directory
254 * @v9ses: 9P2000 session information
255 * @fid: fid to create
256 * @name: name of the file or directory to create
257 * @perm: permissions to create with
258 * @mode: mode to open file (R, RW, etc)
259 * @fcall: pointer to response fcall
260 *
261 */
262
263int
264v9fs_t_create(struct v9fs_session_info *v9ses, u32 fid, char *name,
265 u32 perm, u8 mode, struct v9fs_fcall **fcall)
266{
267 struct v9fs_fcall msg;
268
269 dprintk(DEBUG_9P, "fid %d name '%s' perm %x mode %d\n",
270 fid, name, perm, mode);
271
272 msg.id = TCREATE;
273 msg.params.tcreate.fid = fid;
274 msg.params.tcreate.name = name;
275 msg.params.tcreate.perm = perm;
276 msg.params.tcreate.mode = mode;
277
278 return v9fs_mux_rpc(v9ses, &msg, fcall);
279}
280
281/**
282 * v9fs_t_read - read data
283 * @v9ses: 9P2000 session information
284 * @fid: fid to read from
285 * @offset: offset to start read at
286 * @count: how many bytes to read
287 * @fcall: pointer to response fcall (with data)
288 *
289 */
290
291int
292v9fs_t_read(struct v9fs_session_info *v9ses, u32 fid, u64 offset,
293 u32 count, struct v9fs_fcall **fcall)
294{
295 struct v9fs_fcall msg;
296 struct v9fs_fcall *rc = NULL;
297 long errorno = -1;
298
299 dprintk(DEBUG_9P, "fid %d offset 0x%lx count 0x%x\n", fid,
300 (long unsigned int)offset, count);
301 msg.id = TREAD;
302 msg.params.tread.fid = fid;
303 msg.params.tread.offset = offset;
304 msg.params.tread.count = count;
305 errorno = v9fs_mux_rpc(v9ses, &msg, &rc);
306
307 if (!errorno) {
308 errorno = rc->params.rread.count;
309 dump_data(rc->params.rread.data, rc->params.rread.count);
310 }
311
312 if (fcall)
313 *fcall = rc;
314 else
315 kfree(rc);
316
317 return errorno;
318}
319
320/**
321 * v9fs_t_write - write data
322 * @v9ses: 9P2000 session information
323 * @fid: fid to write to
324 * @offset: offset to start write at
325 * @count: how many bytes to write
326 * @fcall: pointer to response fcall
327 *
328 */
329
330int
331v9fs_t_write(struct v9fs_session_info *v9ses, u32 fid,
332 u64 offset, u32 count, void *data, struct v9fs_fcall **fcall)
333{
334 struct v9fs_fcall msg;
335 struct v9fs_fcall *rc = NULL;
336 long errorno = -1;
337
338 dprintk(DEBUG_9P, "fid %d offset 0x%llx count 0x%x\n", fid,
339 (unsigned long long)offset, count);
340 dump_data(data, count);
341
342 msg.id = TWRITE;
343 msg.params.twrite.fid = fid;
344 msg.params.twrite.offset = offset;
345 msg.params.twrite.count = count;
346 msg.params.twrite.data = data;
347
348 errorno = v9fs_mux_rpc(v9ses, &msg, &rc);
349
350 if (!errorno)
351 errorno = rc->params.rwrite.count;
352
353 if (fcall)
354 *fcall = rc;
355 else
356 kfree(rc);
357
358 return errorno;
359}
diff --git a/fs/9p/9p.h b/fs/9p/9p.h
new file mode 100644
index 0000000000..f55424216b
--- /dev/null
+++ b/fs/9p/9p.h
@@ -0,0 +1,341 @@
1/*
2 * linux/fs/9p/9p.h
3 *
4 * 9P protocol definitions.
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/* Message Types */
28enum {
29 TVERSION = 100,
30 RVERSION,
31 TAUTH = 102,
32 RAUTH,
33 TATTACH = 104,
34 RATTACH,
35 TERROR = 106,
36 RERROR,
37 TFLUSH = 108,
38 RFLUSH,
39 TWALK = 110,
40 RWALK,
41 TOPEN = 112,
42 ROPEN,
43 TCREATE = 114,
44 RCREATE,
45 TREAD = 116,
46 RREAD,
47 TWRITE = 118,
48 RWRITE,
49 TCLUNK = 120,
50 RCLUNK,
51 TREMOVE = 122,
52 RREMOVE,
53 TSTAT = 124,
54 RSTAT,
55 TWSTAT = 126,
56 RWSTAT,
57};
58
59/* modes */
60enum {
61 V9FS_OREAD = 0x00,
62 V9FS_OWRITE = 0x01,
63 V9FS_ORDWR = 0x02,
64 V9FS_OEXEC = 0x03,
65 V9FS_OEXCL = 0x04,
66 V9FS_OTRUNC = 0x10,
67 V9FS_OREXEC = 0x20,
68 V9FS_ORCLOSE = 0x40,
69 V9FS_OAPPEND = 0x80,
70};
71
72/* permissions */
73enum {
74 V9FS_DMDIR = 0x80000000,
75 V9FS_DMAPPEND = 0x40000000,
76 V9FS_DMEXCL = 0x20000000,
77 V9FS_DMMOUNT = 0x10000000,
78 V9FS_DMAUTH = 0x08000000,
79 V9FS_DMTMP = 0x04000000,
80 V9FS_DMSYMLINK = 0x02000000,
81 V9FS_DMLINK = 0x01000000,
82 /* 9P2000.u extensions */
83 V9FS_DMDEVICE = 0x00800000,
84 V9FS_DMNAMEDPIPE = 0x00200000,
85 V9FS_DMSOCKET = 0x00100000,
86 V9FS_DMSETUID = 0x00080000,
87 V9FS_DMSETGID = 0x00040000,
88};
89
90/* qid.types */
91enum {
92 V9FS_QTDIR = 0x80,
93 V9FS_QTAPPEND = 0x40,
94 V9FS_QTEXCL = 0x20,
95 V9FS_QTMOUNT = 0x10,
96 V9FS_QTAUTH = 0x08,
97 V9FS_QTTMP = 0x04,
98 V9FS_QTSYMLINK = 0x02,
99 V9FS_QTLINK = 0x01,
100 V9FS_QTFILE = 0x00,
101};
102
103/* ample room for Twrite/Rread header (iounit) */
104#define V9FS_IOHDRSZ 24
105
106/* qids are the unique ID for a file (like an inode */
107struct v9fs_qid {
108 u8 type;
109 u32 version;
110 u64 path;
111};
112
113/* Plan 9 file metadata (stat) structure */
114struct v9fs_stat {
115 u16 size;
116 u16 type;
117 u32 dev;
118 struct v9fs_qid qid;
119 u32 mode;
120 u32 atime;
121 u32 mtime;
122 u64 length;
123 char *name;
124 char *uid;
125 char *gid;
126 char *muid;
127 char *extension; /* 9p2000.u extensions */
128 u32 n_uid; /* 9p2000.u extensions */
129 u32 n_gid; /* 9p2000.u extensions */
130 u32 n_muid; /* 9p2000.u extensions */
131 char data[0];
132};
133
134/* Structures for Protocol Operations */
135
136struct Tversion {
137 u32 msize;
138 char *version;
139};
140
141struct Rversion {
142 u32 msize;
143 char *version;
144};
145
146struct Tauth {
147 u32 afid;
148 char *uname;
149 char *aname;
150};
151
152struct Rauth {
153 struct v9fs_qid qid;
154};
155
156struct Rerror {
157 char *error;
158 u32 errno; /* 9p2000.u extension */
159};
160
161struct Tflush {
162 u32 oldtag;
163};
164
165struct Rflush {
166};
167
168struct Tattach {
169 u32 fid;
170 u32 afid;
171 char *uname;
172 char *aname;
173};
174
175struct Rattach {
176 struct v9fs_qid qid;
177};
178
179struct Twalk {
180 u32 fid;
181 u32 newfid;
182 u32 nwname;
183 char **wnames;
184};
185
186struct Rwalk {
187 u32 nwqid;
188 struct v9fs_qid *wqids;
189};
190
191struct Topen {
192 u32 fid;
193 u8 mode;
194};
195
196struct Ropen {
197 struct v9fs_qid qid;
198 u32 iounit;
199};
200
201struct Tcreate {
202 u32 fid;
203 char *name;
204 u32 perm;
205 u8 mode;
206};
207
208struct Rcreate {
209 struct v9fs_qid qid;
210 u32 iounit;
211};
212
213struct Tread {
214 u32 fid;
215 u64 offset;
216 u32 count;
217};
218
219struct Rread {
220 u32 count;
221 u8 *data;
222};
223
224struct Twrite {
225 u32 fid;
226 u64 offset;
227 u32 count;
228 u8 *data;
229};
230
231struct Rwrite {
232 u32 count;
233};
234
235struct Tclunk {
236 u32 fid;
237};
238
239struct Rclunk {
240};
241
242struct Tremove {
243 u32 fid;
244};
245
246struct Rremove {
247};
248
249struct Tstat {
250 u32 fid;
251};
252
253struct Rstat {
254 struct v9fs_stat *stat;
255};
256
257struct Twstat {
258 u32 fid;
259 struct v9fs_stat *stat;
260};
261
262struct Rwstat {
263};
264
265/*
266 * fcall is the primary packet structure
267 *
268 */
269
270struct v9fs_fcall {
271 u32 size;
272 u8 id;
273 u16 tag;
274
275 union {
276 struct Tversion tversion;
277 struct Rversion rversion;
278 struct Tauth tauth;
279 struct Rauth rauth;
280 struct Rerror rerror;
281 struct Tflush tflush;
282 struct Rflush rflush;
283 struct Tattach tattach;
284 struct Rattach rattach;
285 struct Twalk twalk;
286 struct Rwalk rwalk;
287 struct Topen topen;
288 struct Ropen ropen;
289 struct Tcreate tcreate;
290 struct Rcreate rcreate;
291 struct Tread tread;
292 struct Rread rread;
293 struct Twrite twrite;
294 struct Rwrite rwrite;
295 struct Tclunk tclunk;
296 struct Rclunk rclunk;
297 struct Tremove tremove;
298 struct Rremove rremove;
299 struct Tstat tstat;
300 struct Rstat rstat;
301 struct Twstat twstat;
302 struct Rwstat rwstat;
303 } params;
304};
305
306#define FCALL_ERROR(fcall) (fcall ? fcall->params.rerror.error : "")
307
308int v9fs_t_version(struct v9fs_session_info *v9ses, u32 msize,
309 char *version, struct v9fs_fcall **rcall);
310
311int v9fs_t_attach(struct v9fs_session_info *v9ses, char *uname, char *aname,
312 u32 fid, u32 afid, struct v9fs_fcall **rcall);
313
314int v9fs_t_clunk(struct v9fs_session_info *v9ses, u32 fid,
315 struct v9fs_fcall **rcall);
316
317int v9fs_t_flush(struct v9fs_session_info *v9ses, u16 oldtag);
318
319int v9fs_t_stat(struct v9fs_session_info *v9ses, u32 fid,
320 struct v9fs_fcall **rcall);
321
322int v9fs_t_wstat(struct v9fs_session_info *v9ses, u32 fid,
323 struct v9fs_stat *stat, struct v9fs_fcall **rcall);
324
325int v9fs_t_walk(struct v9fs_session_info *v9ses, u32 fid, u32 newfid,
326 char *name, struct v9fs_fcall **rcall);
327
328int v9fs_t_open(struct v9fs_session_info *v9ses, u32 fid, u8 mode,
329 struct v9fs_fcall **rcall);
330
331int v9fs_t_remove(struct v9fs_session_info *v9ses, u32 fid,
332 struct v9fs_fcall **rcall);
333
334int v9fs_t_create(struct v9fs_session_info *v9ses, u32 fid, char *name,
335 u32 perm, u8 mode, struct v9fs_fcall **rcall);
336
337int v9fs_t_read(struct v9fs_session_info *v9ses, u32 fid,
338 u64 offset, u32 count, struct v9fs_fcall **rcall);
339
340int v9fs_t_write(struct v9fs_session_info *v9ses, u32 fid, u64 offset,
341 u32 count, void *data, struct v9fs_fcall **rcall);
diff --git a/fs/9p/Makefile b/fs/9p/Makefile
new file mode 100644
index 0000000000..e4e4ffe5a7
--- /dev/null
+++ b/fs/9p/Makefile
@@ -0,0 +1,17 @@
1obj-$(CONFIG_9P_FS) := 9p2000.o
2
39p2000-objs := \
4 vfs_super.o \
5 vfs_inode.o \
6 vfs_file.o \
7 vfs_dir.o \
8 vfs_dentry.o \
9 error.o \
10 mux.o \
11 trans_fd.o \
12 trans_sock.o \
13 9p.o \
14 conv.o \
15 v9fs.o \
16 fid.o
17
diff --git a/fs/9p/conv.c b/fs/9p/conv.c
new file mode 100644
index 0000000000..1554731bd6
--- /dev/null
+++ b/fs/9p/conv.c
@@ -0,0 +1,693 @@
1/*
2 * linux/fs/9p/conv.c
3 *
4 * 9P protocol conversion functions
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/config.h>
28#include <linux/module.h>
29#include <linux/errno.h>
30#include <linux/fs.h>
31#include <linux/idr.h>
32
33#include "debug.h"
34#include "v9fs.h"
35#include "9p.h"
36#include "conv.h"
37
38/*
39 * Buffer to help with string parsing
40 */
41struct cbuf {
42 unsigned char *sp;
43 unsigned char *p;
44 unsigned char *ep;
45};
46
47static inline void buf_init(struct cbuf *buf, void *data, int datalen)
48{
49 buf->sp = buf->p = data;
50 buf->ep = data + datalen;
51}
52
53static inline int buf_check_overflow(struct cbuf *buf)
54{
55 return buf->p > buf->ep;
56}
57
58static inline void buf_check_size(struct cbuf *buf, int len)
59{
60 if (buf->p+len > buf->ep) {
61 if (buf->p < buf->ep) {
62 eprintk(KERN_ERR, "buffer overflow\n");
63 buf->p = buf->ep + 1;
64 }
65 }
66}
67
68static inline void *buf_alloc(struct cbuf *buf, int len)
69{
70 void *ret = NULL;
71
72 buf_check_size(buf, len);
73 ret = buf->p;
74 buf->p += len;
75
76 return ret;
77}
78
79static inline void buf_put_int8(struct cbuf *buf, u8 val)
80{
81 buf_check_size(buf, 1);
82
83 buf->p[0] = val;
84 buf->p++;
85}
86
87static inline void buf_put_int16(struct cbuf *buf, u16 val)
88{
89 buf_check_size(buf, 2);
90
91 *(__le16 *) buf->p = cpu_to_le16(val);
92 buf->p += 2;
93}
94
95static inline void buf_put_int32(struct cbuf *buf, u32 val)
96{
97 buf_check_size(buf, 4);
98
99 *(__le32 *)buf->p = cpu_to_le32(val);
100 buf->p += 4;
101}
102
103static inline void buf_put_int64(struct cbuf *buf, u64 val)
104{
105 buf_check_size(buf, 8);
106
107 *(__le64 *)buf->p = cpu_to_le64(val);
108 buf->p += 8;
109}
110
111static inline void buf_put_stringn(struct cbuf *buf, const char *s, u16 slen)
112{
113 buf_check_size(buf, slen + 2);
114
115 buf_put_int16(buf, slen);
116 memcpy(buf->p, s, slen);
117 buf->p += slen;
118}
119
120static inline void buf_put_string(struct cbuf *buf, const char *s)
121{
122 buf_put_stringn(buf, s, strlen(s));
123}
124
125static inline void buf_put_data(struct cbuf *buf, void *data, u32 datalen)
126{
127 buf_check_size(buf, datalen);
128
129 memcpy(buf->p, data, datalen);
130 buf->p += datalen;
131}
132
133static inline u8 buf_get_int8(struct cbuf *buf)
134{
135 u8 ret = 0;
136
137 buf_check_size(buf, 1);
138 ret = buf->p[0];
139
140 buf->p++;
141
142 return ret;
143}
144
145static inline u16 buf_get_int16(struct cbuf *buf)
146{
147 u16 ret = 0;
148
149 buf_check_size(buf, 2);
150 ret = le16_to_cpu(*(__le16 *)buf->p);
151
152 buf->p += 2;
153
154 return ret;
155}
156
157static inline u32 buf_get_int32(struct cbuf *buf)
158{
159 u32 ret = 0;
160
161 buf_check_size(buf, 4);
162 ret = le32_to_cpu(*(__le32 *)buf->p);
163
164 buf->p += 4;
165
166 return ret;
167}
168
169static inline u64 buf_get_int64(struct cbuf *buf)
170{
171 u64 ret = 0;
172
173 buf_check_size(buf, 8);
174 ret = le64_to_cpu(*(__le64 *)buf->p);
175
176 buf->p += 8;
177
178 return ret;
179}
180
181static inline int
182buf_get_string(struct cbuf *buf, char *data, unsigned int datalen)
183{
184
185 u16 len = buf_get_int16(buf);
186 buf_check_size(buf, len);
187 if (len + 1 > datalen)
188 return 0;
189
190 memcpy(data, buf->p, len);
191 data[len] = 0;
192 buf->p += len;
193
194 return len + 1;
195}
196
197static inline char *buf_get_stringb(struct cbuf *buf, struct cbuf *sbuf)
198{
199 char *ret = NULL;
200 int n = buf_get_string(buf, sbuf->p, sbuf->ep - sbuf->p);
201
202 if (n > 0) {
203 ret = sbuf->p;
204 sbuf->p += n;
205 }
206
207 return ret;
208}
209
210static inline int buf_get_data(struct cbuf *buf, void *data, int datalen)
211{
212 buf_check_size(buf, datalen);
213
214 memcpy(data, buf->p, datalen);
215 buf->p += datalen;
216
217 return datalen;
218}
219
220static inline void *buf_get_datab(struct cbuf *buf, struct cbuf *dbuf,
221 int datalen)
222{
223 char *ret = NULL;
224 int n = 0;
225
226 buf_check_size(dbuf, datalen);
227
228 n = buf_get_data(buf, dbuf->p, datalen);
229
230 if (n > 0) {
231 ret = dbuf->p;
232 dbuf->p += n;
233 }
234
235 return ret;
236}
237
238/**
239 * v9fs_size_stat - calculate the size of a variable length stat struct
240 * @v9ses: session information
241 * @stat: metadata (stat) structure
242 *
243 */
244
245static int v9fs_size_stat(struct v9fs_session_info *v9ses,
246 struct v9fs_stat *stat)
247{
248 int size = 0;
249
250 if (stat == NULL) {
251 eprintk(KERN_ERR, "v9fs_size_stat: got a NULL stat pointer\n");
252 return 0;
253 }
254
255 size = /* 2 + *//* size[2] */
256 2 + /* type[2] */
257 4 + /* dev[4] */
258 1 + /* qid.type[1] */
259 4 + /* qid.vers[4] */
260 8 + /* qid.path[8] */
261 4 + /* mode[4] */
262 4 + /* atime[4] */
263 4 + /* mtime[4] */
264 8 + /* length[8] */
265 8; /* minimum sum of string lengths */
266
267 if (stat->name)
268 size += strlen(stat->name);
269 if (stat->uid)
270 size += strlen(stat->uid);
271 if (stat->gid)
272 size += strlen(stat->gid);
273 if (stat->muid)
274 size += strlen(stat->muid);
275
276 if (v9ses->extended) {
277 size += 4 + /* n_uid[4] */
278 4 + /* n_gid[4] */
279 4 + /* n_muid[4] */
280 2; /* string length of extension[4] */
281 if (stat->extension)
282 size += strlen(stat->extension);
283 }
284
285 return size;
286}
287
288/**
289 * serialize_stat - safely format a stat structure for transmission
290 * @v9ses: session info
291 * @stat: metadata (stat) structure
292 * @bufp: buffer to serialize structure into
293 *
294 */
295
296static int
297serialize_stat(struct v9fs_session_info *v9ses, struct v9fs_stat *stat,
298 struct cbuf *bufp)
299{
300 buf_put_int16(bufp, stat->size);
301 buf_put_int16(bufp, stat->type);
302 buf_put_int32(bufp, stat->dev);
303 buf_put_int8(bufp, stat->qid.type);
304 buf_put_int32(bufp, stat->qid.version);
305 buf_put_int64(bufp, stat->qid.path);
306 buf_put_int32(bufp, stat->mode);
307 buf_put_int32(bufp, stat->atime);
308 buf_put_int32(bufp, stat->mtime);
309 buf_put_int64(bufp, stat->length);
310
311 buf_put_string(bufp, stat->name);
312 buf_put_string(bufp, stat->uid);
313 buf_put_string(bufp, stat->gid);
314 buf_put_string(bufp, stat->muid);
315
316 if (v9ses->extended) {
317 buf_put_string(bufp, stat->extension);
318 buf_put_int32(bufp, stat->n_uid);
319 buf_put_int32(bufp, stat->n_gid);
320 buf_put_int32(bufp, stat->n_muid);
321 }
322
323 if (buf_check_overflow(bufp))
324 return 0;
325
326 return stat->size;
327}
328
329/**
330 * deserialize_stat - safely decode a recieved metadata (stat) structure
331 * @v9ses: session info
332 * @bufp: buffer to deserialize
333 * @stat: metadata (stat) structure
334 * @dbufp: buffer to deserialize variable strings into
335 *
336 */
337
338static inline int
339deserialize_stat(struct v9fs_session_info *v9ses, struct cbuf *bufp,
340 struct v9fs_stat *stat, struct cbuf *dbufp)
341{
342
343 stat->size = buf_get_int16(bufp);
344 stat->type = buf_get_int16(bufp);
345 stat->dev = buf_get_int32(bufp);
346 stat->qid.type = buf_get_int8(bufp);
347 stat->qid.version = buf_get_int32(bufp);
348 stat->qid.path = buf_get_int64(bufp);
349 stat->mode = buf_get_int32(bufp);
350 stat->atime = buf_get_int32(bufp);
351 stat->mtime = buf_get_int32(bufp);
352 stat->length = buf_get_int64(bufp);
353 stat->name = buf_get_stringb(bufp, dbufp);
354 stat->uid = buf_get_stringb(bufp, dbufp);
355 stat->gid = buf_get_stringb(bufp, dbufp);
356 stat->muid = buf_get_stringb(bufp, dbufp);
357
358 if (v9ses->extended) {
359 stat->extension = buf_get_stringb(bufp, dbufp);
360 stat->n_uid = buf_get_int32(bufp);
361 stat->n_gid = buf_get_int32(bufp);
362 stat->n_muid = buf_get_int32(bufp);
363 }
364
365 if (buf_check_overflow(bufp) || buf_check_overflow(dbufp))
366 return 0;
367
368 return stat->size + 2;
369}
370
371/**
372 * deserialize_statb - wrapper for decoding a received metadata structure
373 * @v9ses: session info
374 * @bufp: buffer to deserialize
375 * @dbufp: buffer to deserialize variable strings into
376 *
377 */
378
379static inline struct v9fs_stat *deserialize_statb(struct v9fs_session_info
380 *v9ses, struct cbuf *bufp,
381 struct cbuf *dbufp)
382{
383 struct v9fs_stat *ret = buf_alloc(dbufp, sizeof(struct v9fs_stat));
384
385 if (ret) {
386 int n = deserialize_stat(v9ses, bufp, ret, dbufp);
387 if (n <= 0)
388 return NULL;
389 }
390
391 return ret;
392}
393
394/**
395 * v9fs_deserialize_stat - decode a received metadata structure
396 * @v9ses: session info
397 * @buf: buffer to deserialize
398 * @buflen: length of received buffer
399 * @stat: metadata structure to decode into
400 * @statlen: length of destination metadata structure
401 *
402 */
403
404int
405v9fs_deserialize_stat(struct v9fs_session_info *v9ses, void *buf,
406 u32 buflen, struct v9fs_stat *stat, u32 statlen)
407{
408 struct cbuf buffer;
409 struct cbuf *bufp = &buffer;
410 struct cbuf dbuffer;
411 struct cbuf *dbufp = &dbuffer;
412
413 buf_init(bufp, buf, buflen);
414 buf_init(dbufp, (char *)stat + sizeof(struct v9fs_stat),
415 statlen - sizeof(struct v9fs_stat));
416
417 return deserialize_stat(v9ses, bufp, stat, dbufp);
418}
419
420static inline int
421v9fs_size_fcall(struct v9fs_session_info *v9ses, struct v9fs_fcall *fcall)
422{
423 int size = 4 + 1 + 2; /* size[4] msg[1] tag[2] */
424 int i = 0;
425
426 switch (fcall->id) {
427 default:
428 eprintk(KERN_ERR, "bad msg type %d\n", fcall->id);
429 return 0;
430 case TVERSION: /* msize[4] version[s] */
431 size += 4 + 2 + strlen(fcall->params.tversion.version);
432 break;
433 case TAUTH: /* afid[4] uname[s] aname[s] */
434 size += 4 + 2 + strlen(fcall->params.tauth.uname) +
435 2 + strlen(fcall->params.tauth.aname);
436 break;
437 case TFLUSH: /* oldtag[2] */
438 size += 2;
439 break;
440 case TATTACH: /* fid[4] afid[4] uname[s] aname[s] */
441 size += 4 + 4 + 2 + strlen(fcall->params.tattach.uname) +
442 2 + strlen(fcall->params.tattach.aname);
443 break;
444 case TWALK: /* fid[4] newfid[4] nwname[2] nwname*(wname[s]) */
445 size += 4 + 4 + 2;
446 /* now compute total for the array of names */
447 for (i = 0; i < fcall->params.twalk.nwname; i++)
448 size += 2 + strlen(fcall->params.twalk.wnames[i]);
449 break;
450 case TOPEN: /* fid[4] mode[1] */
451 size += 4 + 1;
452 break;
453 case TCREATE: /* fid[4] name[s] perm[4] mode[1] */
454 size += 4 + 2 + strlen(fcall->params.tcreate.name) + 4 + 1;
455 break;
456 case TREAD: /* fid[4] offset[8] count[4] */
457 size += 4 + 8 + 4;
458 break;
459 case TWRITE: /* fid[4] offset[8] count[4] data[count] */
460 size += 4 + 8 + 4 + fcall->params.twrite.count;
461 break;
462 case TCLUNK: /* fid[4] */
463 size += 4;
464 break;
465 case TREMOVE: /* fid[4] */
466 size += 4;
467 break;
468 case TSTAT: /* fid[4] */
469 size += 4;
470 break;
471 case TWSTAT: /* fid[4] stat[n] */
472 fcall->params.twstat.stat->size =
473 v9fs_size_stat(v9ses, fcall->params.twstat.stat);
474 size += 4 + 2 + 2 + fcall->params.twstat.stat->size;
475 }
476 return size;
477}
478
479/*
480 * v9fs_serialize_fcall - marshall fcall struct into a packet
481 * @v9ses: session information
482 * @fcall: structure to convert
483 * @data: buffer to serialize fcall into
484 * @datalen: length of buffer to serialize fcall into
485 *
486 */
487
488int
489v9fs_serialize_fcall(struct v9fs_session_info *v9ses, struct v9fs_fcall *fcall,
490 void *data, u32 datalen)
491{
492 int i = 0;
493 struct v9fs_stat *stat = NULL;
494 struct cbuf buffer;
495 struct cbuf *bufp = &buffer;
496
497 buf_init(bufp, data, datalen);
498
499 if (!fcall) {
500 eprintk(KERN_ERR, "no fcall\n");
501 return -EINVAL;
502 }
503
504 fcall->size = v9fs_size_fcall(v9ses, fcall);
505
506 buf_put_int32(bufp, fcall->size);
507 buf_put_int8(bufp, fcall->id);
508 buf_put_int16(bufp, fcall->tag);
509
510 dprintk(DEBUG_CONV, "size %d id %d tag %d\n", fcall->size, fcall->id,
511 fcall->tag);
512
513 /* now encode it */
514 switch (fcall->id) {
515 default:
516 eprintk(KERN_ERR, "bad msg type: %d\n", fcall->id);
517 return -EPROTO;
518 case TVERSION:
519 buf_put_int32(bufp, fcall->params.tversion.msize);
520 buf_put_string(bufp, fcall->params.tversion.version);
521 break;
522 case TAUTH:
523 buf_put_int32(bufp, fcall->params.tauth.afid);
524 buf_put_string(bufp, fcall->params.tauth.uname);
525 buf_put_string(bufp, fcall->params.tauth.aname);
526 break;
527 case TFLUSH:
528 buf_put_int16(bufp, fcall->params.tflush.oldtag);
529 break;
530 case TATTACH:
531 buf_put_int32(bufp, fcall->params.tattach.fid);
532 buf_put_int32(bufp, fcall->params.tattach.afid);
533 buf_put_string(bufp, fcall->params.tattach.uname);
534 buf_put_string(bufp, fcall->params.tattach.aname);
535 break;
536 case TWALK:
537 buf_put_int32(bufp, fcall->params.twalk.fid);
538 buf_put_int32(bufp, fcall->params.twalk.newfid);
539 buf_put_int16(bufp, fcall->params.twalk.nwname);
540 for (i = 0; i < fcall->params.twalk.nwname; i++)
541 buf_put_string(bufp, fcall->params.twalk.wnames[i]);
542 break;
543 case TOPEN:
544 buf_put_int32(bufp, fcall->params.topen.fid);
545 buf_put_int8(bufp, fcall->params.topen.mode);
546 break;
547 case TCREATE:
548 buf_put_int32(bufp, fcall->params.tcreate.fid);
549 buf_put_string(bufp, fcall->params.tcreate.name);
550 buf_put_int32(bufp, fcall->params.tcreate.perm);
551 buf_put_int8(bufp, fcall->params.tcreate.mode);
552 break;
553 case TREAD:
554 buf_put_int32(bufp, fcall->params.tread.fid);
555 buf_put_int64(bufp, fcall->params.tread.offset);
556 buf_put_int32(bufp, fcall->params.tread.count);
557 break;
558 case TWRITE:
559 buf_put_int32(bufp, fcall->params.twrite.fid);
560 buf_put_int64(bufp, fcall->params.twrite.offset);
561 buf_put_int32(bufp, fcall->params.twrite.count);
562 buf_put_data(bufp, fcall->params.twrite.data,
563 fcall->params.twrite.count);
564 break;
565 case TCLUNK:
566 buf_put_int32(bufp, fcall->params.tclunk.fid);
567 break;
568 case TREMOVE:
569 buf_put_int32(bufp, fcall->params.tremove.fid);
570 break;
571 case TSTAT:
572 buf_put_int32(bufp, fcall->params.tstat.fid);
573 break;
574 case TWSTAT:
575 buf_put_int32(bufp, fcall->params.twstat.fid);
576 stat = fcall->params.twstat.stat;
577
578 buf_put_int16(bufp, stat->size + 2);
579 serialize_stat(v9ses, stat, bufp);
580 break;
581 }
582
583 if (buf_check_overflow(bufp))
584 return -EIO;
585
586 return fcall->size;
587}
588
589/**
590 * deserialize_fcall - unmarshal a response
591 * @v9ses: session information
592 * @msgsize: size of rcall message
593 * @buf: recieved buffer
594 * @buflen: length of received buffer
595 * @rcall: fcall structure to populate
596 * @rcalllen: length of fcall structure to populate
597 *
598 */
599
600int
601v9fs_deserialize_fcall(struct v9fs_session_info *v9ses, u32 msgsize,
602 void *buf, u32 buflen, struct v9fs_fcall *rcall,
603 int rcalllen)
604{
605
606 struct cbuf buffer;
607 struct cbuf *bufp = &buffer;
608 struct cbuf dbuffer;
609 struct cbuf *dbufp = &dbuffer;
610 int i = 0;
611
612 buf_init(bufp, buf, buflen);
613 buf_init(dbufp, (char *)rcall + sizeof(struct v9fs_fcall),
614 rcalllen - sizeof(struct v9fs_fcall));
615
616 rcall->size = msgsize;
617 rcall->id = buf_get_int8(bufp);
618 rcall->tag = buf_get_int16(bufp);
619
620 dprintk(DEBUG_CONV, "size %d id %d tag %d\n", rcall->size, rcall->id,
621 rcall->tag);
622 switch (rcall->id) {
623 default:
624 eprintk(KERN_ERR, "unknown message type: %d\n", rcall->id);
625 return -EPROTO;
626 case RVERSION:
627 rcall->params.rversion.msize = buf_get_int32(bufp);
628 rcall->params.rversion.version = buf_get_stringb(bufp, dbufp);
629 break;
630 case RFLUSH:
631 break;
632 case RATTACH:
633 rcall->params.rattach.qid.type = buf_get_int8(bufp);
634 rcall->params.rattach.qid.version = buf_get_int32(bufp);
635 rcall->params.rattach.qid.path = buf_get_int64(bufp);
636 break;
637 case RWALK:
638 rcall->params.rwalk.nwqid = buf_get_int16(bufp);
639 rcall->params.rwalk.wqids = buf_alloc(bufp,
640 rcall->params.rwalk.nwqid * sizeof(struct v9fs_qid));
641 if (rcall->params.rwalk.wqids)
642 for (i = 0; i < rcall->params.rwalk.nwqid; i++) {
643 rcall->params.rwalk.wqids[i].type =
644 buf_get_int8(bufp);
645 rcall->params.rwalk.wqids[i].version =
646 buf_get_int16(bufp);
647 rcall->params.rwalk.wqids[i].path =
648 buf_get_int64(bufp);
649 }
650 break;
651 case ROPEN:
652 rcall->params.ropen.qid.type = buf_get_int8(bufp);
653 rcall->params.ropen.qid.version = buf_get_int32(bufp);
654 rcall->params.ropen.qid.path = buf_get_int64(bufp);
655 rcall->params.ropen.iounit = buf_get_int32(bufp);
656 break;
657 case RCREATE:
658 rcall->params.rcreate.qid.type = buf_get_int8(bufp);
659 rcall->params.rcreate.qid.version = buf_get_int32(bufp);
660 rcall->params.rcreate.qid.path = buf_get_int64(bufp);
661 rcall->params.rcreate.iounit = buf_get_int32(bufp);
662 break;
663 case RREAD:
664 rcall->params.rread.count = buf_get_int32(bufp);
665 rcall->params.rread.data = buf_get_datab(bufp, dbufp,
666 rcall->params.rread.count);
667 break;
668 case RWRITE:
669 rcall->params.rwrite.count = buf_get_int32(bufp);
670 break;
671 case RCLUNK:
672 break;
673 case RREMOVE:
674 break;
675 case RSTAT:
676 buf_get_int16(bufp);
677 rcall->params.rstat.stat =
678 deserialize_statb(v9ses, bufp, dbufp);
679 break;
680 case RWSTAT:
681 break;
682 case RERROR:
683 rcall->params.rerror.error = buf_get_stringb(bufp, dbufp);
684 if (v9ses->extended)
685 rcall->params.rerror.errno = buf_get_int16(bufp);
686 break;
687 }
688
689 if (buf_check_overflow(bufp) || buf_check_overflow(dbufp))
690 return -EIO;
691
692 return rcall->size;
693}
diff --git a/fs/9p/conv.h b/fs/9p/conv.h
new file mode 100644
index 0000000000..ee849613c6
--- /dev/null
+++ b/fs/9p/conv.h
@@ -0,0 +1,36 @@
1/*
2 * linux/fs/9p/conv.h
3 *
4 * 9P protocol conversion definitions
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
27int v9fs_deserialize_stat(struct v9fs_session_info *, void *buf,
28 u32 buflen, struct v9fs_stat *stat, u32 statlen);
29int v9fs_serialize_fcall(struct v9fs_session_info *, struct v9fs_fcall *tcall,
30 void *buf, u32 buflen);
31int v9fs_deserialize_fcall(struct v9fs_session_info *, u32 msglen,
32 void *buf, u32 buflen, struct v9fs_fcall *rcall,
33 int rcalllen);
34
35/* this one is actually in error.c right now */
36int v9fs_errstr2errno(char *errstr);
diff --git a/fs/9p/debug.h b/fs/9p/debug.h
new file mode 100644
index 0000000000..4445f06919
--- /dev/null
+++ b/fs/9p/debug.h
@@ -0,0 +1,70 @@
1/*
2 * linux/fs/9p/debug.h - V9FS Debug Definitions
3 *
4 * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
5 * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to:
19 * Free Software Foundation
20 * 51 Franklin Street, Fifth Floor
21 * Boston, MA 02111-1301 USA
22 *
23 */
24
25#define DEBUG_ERROR (1<<0)
26#define DEBUG_CURRENT (1<<1)
27#define DEBUG_9P (1<<2)
28#define DEBUG_VFS (1<<3)
29#define DEBUG_CONV (1<<4)
30#define DEBUG_MUX (1<<5)
31#define DEBUG_TRANS (1<<6)
32#define DEBUG_SLABS (1<<7)
33
34#define DEBUG_DUMP_PKT 0
35
36extern int v9fs_debug_level;
37
38#define dprintk(level, format, arg...) \
39do { \
40 if((v9fs_debug_level & level)==level) \
41 printk(KERN_NOTICE "-- %s (%d): " \
42 format , __FUNCTION__, current->pid , ## arg); \
43} while(0)
44
45#define eprintk(level, format, arg...) \
46do { \
47 printk(level "v9fs: %s (%d): " \
48 format , __FUNCTION__, current->pid , ## arg); \
49} while(0)
50
51#if DEBUG_DUMP_PKT
52static inline void dump_data(const unsigned char *data, unsigned int datalen)
53{
54 int i, j;
55 int len = datalen;
56
57 printk(KERN_DEBUG "data ");
58 for (i = 0; i < len; i += 4) {
59 for (j = 0; (j < 4) && (i + j < len); j++)
60 printk(KERN_DEBUG "%02x", data[i + j]);
61 printk(KERN_DEBUG " ");
62 }
63 printk(KERN_DEBUG "\n");
64}
65#else /* DEBUG_DUMP_PKT */
66static inline void dump_data(const unsigned char *data, unsigned int datalen)
67{
68
69}
70#endif /* DEBUG_DUMP_PKT */
diff --git a/fs/9p/error.c b/fs/9p/error.c
new file mode 100644
index 0000000000..fee5d19179
--- /dev/null
+++ b/fs/9p/error.c
@@ -0,0 +1,93 @@
1/*
2 * linux/fs/9p/error.c
3 *
4 * Error string handling
5 *
6 * Plan 9 uses error strings, Unix uses error numbers. These functions
7 * try to help manage that and provide for dynamically adding error
8 * mappings.
9 *
10 * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
11 * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to:
25 * Free Software Foundation
26 * 51 Franklin Street, Fifth Floor
27 * Boston, MA 02111-1301 USA
28 *
29 */
30
31#include <linux/config.h>
32#include <linux/module.h>
33
34#include <linux/list.h>
35#include <linux/jhash.h>
36
37#include "debug.h"
38#include "error.h"
39
40/**
41 * v9fs_error_init - preload
42 * @errstr: error string
43 *
44 */
45
46int v9fs_error_init(void)
47{
48 struct errormap *c;
49 int bucket;
50
51 /* initialize hash table */
52 for (bucket = 0; bucket < ERRHASHSZ; bucket++)
53 INIT_HLIST_HEAD(&hash_errmap[bucket]);
54
55 /* load initial error map into hash table */
56 for (c = errmap; c->name != NULL; c++) {
57 bucket = jhash(c->name, strlen(c->name), 0) % ERRHASHSZ;
58 INIT_HLIST_NODE(&c->list);
59 hlist_add_head(&c->list, &hash_errmap[bucket]);
60 }
61
62 return 1;
63}
64
65/**
66 * errstr2errno - convert error string to error number
67 * @errstr: error string
68 *
69 */
70
71int v9fs_errstr2errno(char *errstr)
72{
73 int errno = 0;
74 struct hlist_node *p = NULL;
75 struct errormap *c = NULL;
76 int bucket = jhash(errstr, strlen(errstr), 0) % ERRHASHSZ;
77
78 hlist_for_each_entry(c, p, &hash_errmap[bucket], list) {
79 if (!strcmp(c->name, errstr)) {
80 errno = c->val;
81 break;
82 }
83 }
84
85 if (errno == 0) {
86 /* TODO: if error isn't found, add it dynamically */
87 printk(KERN_ERR "%s: errstr :%s: not found\n", __FUNCTION__,
88 errstr);
89 errno = 1;
90 }
91
92 return -errno;
93}
diff --git a/fs/9p/error.h b/fs/9p/error.h
new file mode 100644
index 0000000000..78f89acf7c
--- /dev/null
+++ b/fs/9p/error.h
@@ -0,0 +1,178 @@
1/*
2 * linux/fs/9p/error.h
3 *
4 * Huge Nasty Error Table
5 *
6 * Plan 9 uses error strings, Unix uses error numbers. This table tries to
7 * match UNIX strings and Plan 9 strings to unix error numbers. It is used
8 * to preload the dynamic error table which can also track user-specific error
9 * strings.
10 *
11 * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
12 * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to:
26 * Free Software Foundation
27 * 51 Franklin Street, Fifth Floor
28 * Boston, MA 02111-1301 USA
29 *
30 */
31
32#include <linux/errno.h>
33#include <asm/errno.h>
34
35struct errormap {
36 char *name;
37 int val;
38
39 struct hlist_node list;
40};
41
42#define ERRHASHSZ 32
43static struct hlist_head hash_errmap[ERRHASHSZ];
44
45/* FixMe - reduce to a reasonable size */
46static struct errormap errmap[] = {
47 {"Operation not permitted", EPERM},
48 {"wstat prohibited", EPERM},
49 {"No such file or directory", ENOENT},
50 {"directory entry not found", ENOENT},
51 {"file not found", ENOENT},
52 {"Interrupted system call", EINTR},
53 {"Input/output error", EIO},
54 {"No such device or address", ENXIO},
55 {"Argument list too long", E2BIG},
56 {"Bad file descriptor", EBADF},
57 {"Resource temporarily unavailable", EAGAIN},
58 {"Cannot allocate memory", ENOMEM},
59 {"Permission denied", EACCES},
60 {"Bad address", EFAULT},
61 {"Block device required", ENOTBLK},
62 {"Device or resource busy", EBUSY},
63 {"File exists", EEXIST},
64 {"Invalid cross-device link", EXDEV},
65 {"No such device", ENODEV},
66 {"Not a directory", ENOTDIR},
67 {"Is a directory", EISDIR},
68 {"Invalid argument", EINVAL},
69 {"Too many open files in system", ENFILE},
70 {"Too many open files", EMFILE},
71 {"Text file busy", ETXTBSY},
72 {"File too large", EFBIG},
73 {"No space left on device", ENOSPC},
74 {"Illegal seek", ESPIPE},
75 {"Read-only file system", EROFS},
76 {"Too many links", EMLINK},
77 {"Broken pipe", EPIPE},
78 {"Numerical argument out of domain", EDOM},
79 {"Numerical result out of range", ERANGE},
80 {"Resource deadlock avoided", EDEADLK},
81 {"File name too long", ENAMETOOLONG},
82 {"No locks available", ENOLCK},
83 {"Function not implemented", ENOSYS},
84 {"Directory not empty", ENOTEMPTY},
85 {"Too many levels of symbolic links", ELOOP},
86 {"No message of desired type", ENOMSG},
87 {"Identifier removed", EIDRM},
88 {"No data available", ENODATA},
89 {"Machine is not on the network", ENONET},
90 {"Package not installed", ENOPKG},
91 {"Object is remote", EREMOTE},
92 {"Link has been severed", ENOLINK},
93 {"Communication error on send", ECOMM},
94 {"Protocol error", EPROTO},
95 {"Bad message", EBADMSG},
96 {"File descriptor in bad state", EBADFD},
97 {"Streams pipe error", ESTRPIPE},
98 {"Too many users", EUSERS},
99 {"Socket operation on non-socket", ENOTSOCK},
100 {"Message too long", EMSGSIZE},
101 {"Protocol not available", ENOPROTOOPT},
102 {"Protocol not supported", EPROTONOSUPPORT},
103 {"Socket type not supported", ESOCKTNOSUPPORT},
104 {"Operation not supported", EOPNOTSUPP},
105 {"Protocol family not supported", EPFNOSUPPORT},
106 {"Network is down", ENETDOWN},
107 {"Network is unreachable", ENETUNREACH},
108 {"Network dropped connection on reset", ENETRESET},
109 {"Software caused connection abort", ECONNABORTED},
110 {"Connection reset by peer", ECONNRESET},
111 {"No buffer space available", ENOBUFS},
112 {"Transport endpoint is already connected", EISCONN},
113 {"Transport endpoint is not connected", ENOTCONN},
114 {"Cannot send after transport endpoint shutdown", ESHUTDOWN},
115 {"Connection timed out", ETIMEDOUT},
116 {"Connection refused", ECONNREFUSED},
117 {"Host is down", EHOSTDOWN},
118 {"No route to host", EHOSTUNREACH},
119 {"Operation already in progress", EALREADY},
120 {"Operation now in progress", EINPROGRESS},
121 {"Is a named type file", EISNAM},
122 {"Remote I/O error", EREMOTEIO},
123 {"Disk quota exceeded", EDQUOT},
124/* errors from fossil, vacfs, and u9fs */
125 {"fid unknown or out of range", EBADF},
126 {"permission denied", EACCES},
127 {"file does not exist", ENOENT},
128 {"authentication failed", ECONNREFUSED},
129 {"bad offset in directory read", ESPIPE},
130 {"bad use of fid", EBADF},
131 {"wstat can't convert between files and directories", EPERM},
132 {"directory is not empty", ENOTEMPTY},
133 {"file exists", EEXIST},
134 {"file already exists", EEXIST},
135 {"file or directory already exists", EEXIST},
136 {"fid already in use", EBADF},
137 {"file in use", ETXTBSY},
138 {"i/o error", EIO},
139 {"file already open for I/O", ETXTBSY},
140 {"illegal mode", EINVAL},
141 {"illegal name", ENAMETOOLONG},
142 {"not a directory", ENOTDIR},
143 {"not a member of proposed group", EPERM},
144 {"not owner", EACCES},
145 {"only owner can change group in wstat", EACCES},
146 {"read only file system", EROFS},
147 {"no access to special file", EPERM},
148 {"i/o count too large", EIO},
149 {"unknown group", EINVAL},
150 {"unknown user", EINVAL},
151 {"bogus wstat buffer", EPROTO},
152 {"exclusive use file already open", EAGAIN},
153 {"corrupted directory entry", EIO},
154 {"corrupted file entry", EIO},
155 {"corrupted block label", EIO},
156 {"corrupted meta data", EIO},
157 {"illegal offset", EINVAL},
158 {"illegal path element", ENOENT},
159 {"root of file system is corrupted", EIO},
160 {"corrupted super block", EIO},
161 {"protocol botch", EPROTO},
162 {"file system is full", ENOSPC},
163 {"file is in use", EAGAIN},
164 {"directory entry is not allocated", ENOENT},
165 {"file is read only", EROFS},
166 {"file has been removed", EIDRM},
167 {"only support truncation to zero length", EPERM},
168 {"cannot remove root", EPERM},
169 {"file too big", EFBIG},
170 {"venti i/o error", EIO},
171 /* these are not errors */
172 {"u9fs rhostsauth: no authentication required", 0},
173 {"u9fs authnone: no authentication required", 0},
174 {NULL, -1}
175};
176
177extern int v9fs_error_init(void);
178extern int v9fs_errstr2errno(char *errstr);
diff --git a/fs/9p/fid.c b/fs/9p/fid.c
new file mode 100644
index 0000000000..821c9c4d76
--- /dev/null
+++ b/fs/9p/fid.c
@@ -0,0 +1,241 @@
1/*
2 * V9FS FID Management
3 *
4 * Copyright (C) 2005 by Eric Van Hensbergen <ericvh@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to:
18 * Free Software Foundation
19 * 51 Franklin Street, Fifth Floor
20 * Boston, MA 02111-1301 USA
21 *
22 */
23
24#include <linux/config.h>
25#include <linux/module.h>
26#include <linux/errno.h>
27#include <linux/fs.h>
28#include <linux/idr.h>
29
30#include "debug.h"
31#include "v9fs.h"
32#include "9p.h"
33#include "v9fs_vfs.h"
34#include "transport.h"
35#include "mux.h"
36#include "conv.h"
37#include "fid.h"
38
39/**
40 * v9fs_fid_insert - add a fid to a dentry
41 * @fid: fid to add
42 * @dentry: dentry that it is being added to
43 *
44 */
45
46static int v9fs_fid_insert(struct v9fs_fid *fid, struct dentry *dentry)
47{
48 struct list_head *fid_list = (struct list_head *)dentry->d_fsdata;
49 dprintk(DEBUG_9P, "fid %d (%p) dentry %s (%p)\n", fid->fid, fid,
50 dentry->d_iname, dentry);
51 if (dentry->d_fsdata == NULL) {
52 dentry->d_fsdata =
53 kmalloc(sizeof(struct list_head), GFP_KERNEL);
54 if (dentry->d_fsdata == NULL) {
55 dprintk(DEBUG_ERROR, "Out of memory\n");
56 return -ENOMEM;
57 }
58 fid_list = (struct list_head *)dentry->d_fsdata;
59 INIT_LIST_HEAD(fid_list); /* Initialize list head */
60 }
61
62 fid->uid = current->uid;
63 fid->pid = current->pid;
64 list_add(&fid->list, fid_list);
65 return 0;
66}
67
68/**
69 * v9fs_fid_create - allocate a FID structure
70 * @dentry - dentry to link newly created fid to
71 *
72 */
73
74struct v9fs_fid *v9fs_fid_create(struct dentry *dentry)
75{
76 struct v9fs_fid *new;
77
78 new = kmalloc(sizeof(struct v9fs_fid), GFP_KERNEL);
79 if (new == NULL) {
80 dprintk(DEBUG_ERROR, "Out of Memory\n");
81 return ERR_PTR(-ENOMEM);
82 }
83
84 new->fid = -1;
85 new->fidopen = 0;
86 new->fidcreate = 0;
87 new->fidclunked = 0;
88 new->iounit = 0;
89
90 if (v9fs_fid_insert(new, dentry) == 0)
91 return new;
92 else {
93 dprintk(DEBUG_ERROR, "Problems inserting to dentry\n");
94 kfree(new);
95 return NULL;
96 }
97}
98
99/**
100 * v9fs_fid_destroy - deallocate a FID structure
101 * @fid: fid to destroy
102 *
103 */
104
105void v9fs_fid_destroy(struct v9fs_fid *fid)
106{
107 list_del(&fid->list);
108 kfree(fid);
109}
110
111/**
112 * v9fs_fid_lookup - retrieve the right fid from a particular dentry
113 * @dentry: dentry to look for fid in
114 * @type: intent of lookup (operation or traversal)
115 *
116 * search list of fids associated with a dentry for a fid with a matching
117 * thread id or uid. If that fails, look up the dentry's parents to see if you
118 * can find a matching fid.
119 *
120 */
121
122struct v9fs_fid *v9fs_fid_lookup(struct dentry *dentry, int type)
123{
124 struct list_head *fid_list = (struct list_head *)dentry->d_fsdata;
125 struct v9fs_fid *current_fid = NULL;
126 struct v9fs_fid *temp = NULL;
127 struct v9fs_fid *return_fid = NULL;
128 int found_parent = 0;
129 int found_user = 0;
130
131 dprintk(DEBUG_9P, " dentry: %s (%p) type %d\n", dentry->d_iname, dentry,
132 type);
133
134 if (fid_list && !list_empty(fid_list)) {
135 list_for_each_entry_safe(current_fid, temp, fid_list, list) {
136 if (current_fid->uid == current->uid) {
137 if (return_fid == NULL) {
138 if ((type == FID_OP)
139 || (!current_fid->fidopen)) {
140 return_fid = current_fid;
141 found_user = 1;
142 }
143 }
144 }
145 if (current_fid->pid == current->real_parent->pid) {
146 if ((return_fid == NULL) || (found_parent)
147 || (found_user)) {
148 if ((type == FID_OP)
149 || (!current_fid->fidopen)) {
150 return_fid = current_fid;
151 found_parent = 1;
152 found_user = 0;
153 }
154 }
155 }
156 if (current_fid->pid == current->pid) {
157 if ((type == FID_OP) ||
158 (!current_fid->fidopen)) {
159 return_fid = current_fid;
160 found_parent = 0;
161 found_user = 0;
162 }
163 }
164 }
165 }
166
167 /* we are at the root but didn't match */
168 if ((!return_fid) && (dentry->d_parent == dentry)) {
169 /* TODO: clone attach with new uid */
170 return_fid = current_fid;
171 }
172
173 if (!return_fid) {
174 struct dentry *par = current->fs->pwd->d_parent;
175 int count = 1;
176 while (par != NULL) {
177 if (par == dentry)
178 break;
179 count++;
180 if (par == par->d_parent) {
181 dprintk(DEBUG_ERROR,
182 "got to root without finding dentry\n");
183 break;
184 }
185 par = par->d_parent;
186 }
187
188/* XXX - there may be some duplication we can get rid of */
189 if (par == dentry) {
190 /* we need to fid_lookup the starting point */
191 int fidnum = -1;
192 int oldfid = -1;
193 int result = -1;
194 struct v9fs_session_info *v9ses =
195 v9fs_inode2v9ses(current->fs->pwd->d_inode);
196
197 current_fid =
198 v9fs_fid_lookup(current->fs->pwd, FID_WALK);
199 if (current_fid == NULL) {
200 dprintk(DEBUG_ERROR,
201 "process cwd doesn't have a fid\n");
202 return return_fid;
203 }
204 oldfid = current_fid->fid;
205 par = current->fs->pwd;
206 /* TODO: take advantage of multiwalk */
207
208 fidnum = v9fs_get_idpool(&v9ses->fidpool);
209 if (fidnum < 0) {
210 dprintk(DEBUG_ERROR,
211 "could not get a new fid num\n");
212 return return_fid;
213 }
214
215 while (par != dentry) {
216 result =
217 v9fs_t_walk(v9ses, oldfid, fidnum, "..",
218 NULL);
219 if (result < 0) {
220 dprintk(DEBUG_ERROR,
221 "problem walking to parent\n");
222
223 break;
224 }
225 oldfid = fidnum;
226 if (par == par->d_parent) {
227 dprintk(DEBUG_ERROR,
228 "can't find dentry\n");
229 break;
230 }
231 par = par->d_parent;
232 }
233 if (par == dentry) {
234 return_fid = v9fs_fid_create(dentry);
235 return_fid->fid = fidnum;
236 }
237 }
238 }
239
240 return return_fid;
241}
diff --git a/fs/9p/fid.h b/fs/9p/fid.h
new file mode 100644
index 0000000000..7db478ccca
--- /dev/null
+++ b/fs/9p/fid.h
@@ -0,0 +1,57 @@
1/*
2 * V9FS FID Management
3 *
4 * Copyright (C) 2005 by Eric Van Hensbergen <ericvh@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to:
18 * Free Software Foundation
19 * 51 Franklin Street, Fifth Floor
20 * Boston, MA 02111-1301 USA
21 *
22 */
23
24#include <linux/list.h>
25
26#define FID_OP 0
27#define FID_WALK 1
28
29struct v9fs_fid {
30 struct list_head list; /* list of fids associated with a dentry */
31 struct list_head active; /* XXX - debug */
32
33 u32 fid;
34 unsigned char fidopen; /* set when fid is opened */
35 unsigned char fidcreate; /* set when fid was just created */
36 unsigned char fidclunked; /* set when fid has already been clunked */
37
38 struct v9fs_qid qid;
39 u32 iounit;
40
41 /* readdir stuff */
42 int rdir_fpos;
43 loff_t rdir_pos;
44 struct v9fs_fcall *rdir_fcall;
45
46 /* management stuff */
47 pid_t pid; /* thread associated with this fid */
48 uid_t uid; /* user associated with this fid */
49
50 /* private data */
51 struct file *filp; /* backpointer to File struct for open files */
52 struct v9fs_session_info *v9ses; /* session info for this FID */
53};
54
55struct v9fs_fid *v9fs_fid_lookup(struct dentry *dentry, int type);
56void v9fs_fid_destroy(struct v9fs_fid *fid);
57struct v9fs_fid *v9fs_fid_create(struct dentry *);
diff --git a/fs/9p/mux.c b/fs/9p/mux.c
new file mode 100644
index 0000000000..8835b576f7
--- /dev/null
+++ b/fs/9p/mux.c
@@ -0,0 +1,475 @@
1/*
2 * linux/fs/9p/mux.c
3 *
4 * Protocol Multiplexer
5 *
6 * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
7 * Copyright (C) 2004 by Latchesar Ionkov <lucho@ionkov.net>
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/config.h>
28#include <linux/module.h>
29#include <linux/errno.h>
30#include <linux/fs.h>
31#include <linux/kthread.h>
32#include <linux/idr.h>
33
34#include "debug.h"
35#include "v9fs.h"
36#include "9p.h"
37#include "transport.h"
38#include "conv.h"
39#include "mux.h"
40
41/**
42 * dprintcond - print condition of session info
43 * @v9ses: session info structure
44 * @req: RPC request structure
45 *
46 */
47
48static inline int
49dprintcond(struct v9fs_session_info *v9ses, struct v9fs_rpcreq *req)
50{
51 dprintk(DEBUG_MUX, "condition: %d, %p\n", v9ses->transport->status,
52 req->rcall);
53 return 0;
54}
55
56/**
57 * xread - force read of a certain number of bytes
58 * @v9ses: session info structure
59 * @ptr: pointer to buffer
60 * @sz: number of bytes to read
61 *
62 * Chuck Cranor CS-533 project1
63 */
64
65static int xread(struct v9fs_session_info *v9ses, void *ptr, unsigned long sz)
66{
67 int rd = 0;
68 int ret = 0;
69 while (rd < sz) {
70 ret = v9ses->transport->read(v9ses->transport, ptr, sz - rd);
71 if (ret <= 0) {
72 dprintk(DEBUG_ERROR, "xread errno %d\n", ret);
73 return ret;
74 }
75 rd += ret;
76 ptr += ret;
77 }
78 return (rd);
79}
80
81/**
82 * read_message - read a full 9P2000 fcall packet
83 * @v9ses: session info structure
84 * @rcall: fcall structure to read into
85 * @rcalllen: size of fcall buffer
86 *
87 */
88
89static int
90read_message(struct v9fs_session_info *v9ses,
91 struct v9fs_fcall *rcall, int rcalllen)
92{
93 unsigned char buf[4];
94 void *data;
95 int size = 0;
96 int res = 0;
97
98 res = xread(v9ses, buf, sizeof(buf));
99 if (res < 0) {
100 dprintk(DEBUG_ERROR,
101 "Reading of count field failed returned: %d\n", res);
102 return res;
103 }
104
105 if (res < 4) {
106 dprintk(DEBUG_ERROR,
107 "Reading of count field failed returned: %d\n", res);
108 return -EIO;
109 }
110
111 size = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
112 dprintk(DEBUG_MUX, "got a packet count: %d\n", size);
113
114 /* adjust for the four bytes of size */
115 size -= 4;
116
117 if (size > v9ses->maxdata) {
118 dprintk(DEBUG_ERROR, "packet too big: %d\n", size);
119 return -E2BIG;
120 }
121
122 data = kmalloc(size, GFP_KERNEL);
123 if (!data) {
124 eprintk(KERN_WARNING, "out of memory\n");
125 return -ENOMEM;
126 }
127
128 res = xread(v9ses, data, size);
129 if (res < size) {
130 dprintk(DEBUG_ERROR, "Reading of fcall failed returned: %d\n",
131 res);
132 kfree(data);
133 return res;
134 }
135
136 /* we now have an in-memory string that is the reply.
137 * deserialize it. There is very little to go wrong at this point
138 * save for v9fs_alloc errors.
139 */
140 res = v9fs_deserialize_fcall(v9ses, size, data, v9ses->maxdata,
141 rcall, rcalllen);
142
143 kfree(data);
144
145 if (res < 0)
146 return res;
147
148 return 0;
149}
150
151/**
152 * v9fs_recv - receive an RPC response for a particular tag
153 * @v9ses: session info structure
154 * @req: RPC request structure
155 *
156 */
157
158static int v9fs_recv(struct v9fs_session_info *v9ses, struct v9fs_rpcreq *req)
159{
160 int ret = 0;
161
162 dprintk(DEBUG_MUX, "waiting for response: %d\n", req->tcall->tag);
163 ret = wait_event_interruptible(v9ses->read_wait,
164 ((v9ses->transport->status != Connected) ||
165 (req->rcall != 0) || (req->err < 0) ||
166 dprintcond(v9ses, req)));
167
168 dprintk(DEBUG_MUX, "got it: rcall %p\n", req->rcall);
169
170 spin_lock(&v9ses->muxlock);
171 list_del(&req->next);
172 spin_unlock(&v9ses->muxlock);
173
174 if (req->err < 0)
175 return req->err;
176
177 if (v9ses->transport->status == Disconnected)
178 return -ECONNRESET;
179
180 return ret;
181}
182
183/**
184 * v9fs_send - send a 9P request
185 * @v9ses: session info structure
186 * @req: RPC request to send
187 *
188 */
189
190static int v9fs_send(struct v9fs_session_info *v9ses, struct v9fs_rpcreq *req)
191{
192 int ret = -1;
193 void *data = NULL;
194 struct v9fs_fcall *tcall = req->tcall;
195
196 data = kmalloc(v9ses->maxdata + V9FS_IOHDRSZ, GFP_KERNEL);
197 if (!data)
198 return -ENOMEM;
199
200 tcall->size = 0; /* enforce size recalculation */
201 ret =
202 v9fs_serialize_fcall(v9ses, tcall, data,
203 v9ses->maxdata + V9FS_IOHDRSZ);
204 if (ret < 0)
205 goto free_data;
206
207 spin_lock(&v9ses->muxlock);
208 list_add(&req->next, &v9ses->mux_fcalls);
209 spin_unlock(&v9ses->muxlock);
210
211 dprintk(DEBUG_MUX, "sending message: tag %d size %d\n", tcall->tag,
212 tcall->size);
213 ret = v9ses->transport->write(v9ses->transport, data, tcall->size);
214
215 if (ret != tcall->size) {
216 spin_lock(&v9ses->muxlock);
217 list_del(&req->next);
218 kfree(req->rcall);
219
220 spin_unlock(&v9ses->muxlock);
221 if (ret >= 0)
222 ret = -EREMOTEIO;
223 } else
224 ret = 0;
225
226 free_data:
227 kfree(data);
228 return ret;
229}
230
231/**
232 * v9fs_mux_rpc - send a request, receive a response
233 * @v9ses: session info structure
234 * @tcall: fcall to send
235 * @rcall: buffer to place response into
236 *
237 */
238
239long
240v9fs_mux_rpc(struct v9fs_session_info *v9ses, struct v9fs_fcall *tcall,
241 struct v9fs_fcall **rcall)
242{
243 int tid = -1;
244 struct v9fs_fcall *fcall = NULL;
245 struct v9fs_rpcreq req;
246 int ret = -1;
247
248 if (!v9ses)
249 return -EINVAL;
250
251 if (!v9ses->transport || v9ses->transport->status != Connected)
252 return -EIO;
253
254 if (rcall)
255 *rcall = NULL;
256
257 if (tcall->id != TVERSION) {
258 tid = v9fs_get_idpool(&v9ses->tidpool);
259 if (tid < 0)
260 return -ENOMEM;
261 }
262
263 tcall->tag = tid;
264
265 req.tcall = tcall;
266 req.err = 0;
267 req.rcall = NULL;
268
269 ret = v9fs_send(v9ses, &req);
270
271 if (ret < 0) {
272 if (tcall->id != TVERSION)
273 v9fs_put_idpool(tid, &v9ses->tidpool);
274 dprintk(DEBUG_MUX, "error %d\n", ret);
275 return ret;
276 }
277
278 ret = v9fs_recv(v9ses, &req);
279
280 fcall = req.rcall;
281
282 dprintk(DEBUG_MUX, "received: tag=%x, ret=%d\n", tcall->tag, ret);
283 if (ret == -ERESTARTSYS) {
284 if (v9ses->transport->status != Disconnected
285 && tcall->id != TFLUSH) {
286 unsigned long flags;
287
288 dprintk(DEBUG_MUX, "flushing the tag: %d\n",
289 tcall->tag);
290 clear_thread_flag(TIF_SIGPENDING);
291 v9fs_t_flush(v9ses, tcall->tag);
292 spin_lock_irqsave(&current->sighand->siglock, flags);
293 recalc_sigpending();
294 spin_unlock_irqrestore(&current->sighand->siglock,
295 flags);
296 dprintk(DEBUG_MUX, "flushing done\n");
297 }
298
299 goto release_req;
300 } else if (ret < 0)
301 goto release_req;
302
303 if (!fcall)
304 ret = -EIO;
305 else {
306 if (fcall->id == RERROR) {
307 ret = v9fs_errstr2errno(fcall->params.rerror.error);
308 if (ret == 0) { /* string match failed */
309 if (fcall->params.rerror.errno)
310 ret = -(fcall->params.rerror.errno);
311 else
312 ret = -ESERVERFAULT;
313 }
314 } else if (fcall->id != tcall->id + 1) {
315 dprintk(DEBUG_ERROR,
316 "fcall mismatch: expected %d, got %d\n",
317 tcall->id + 1, fcall->id);
318 ret = -EIO;
319 }
320 }
321
322 release_req:
323 if (tcall->id != TVERSION)
324 v9fs_put_idpool(tid, &v9ses->tidpool);
325 if (rcall)
326 *rcall = fcall;
327 else
328 kfree(fcall);
329
330 return ret;
331}
332
333/**
334 * v9fs_mux_cancel_requests - cancels all pending requests
335 *
336 * @v9ses: session info structure
337 * @err: error code to return to the requests
338 */
339void v9fs_mux_cancel_requests(struct v9fs_session_info *v9ses, int err)
340{
341 struct v9fs_rpcreq *rptr;
342 struct v9fs_rpcreq *rreq;
343
344 dprintk(DEBUG_MUX, " %d\n", err);
345 spin_lock(&v9ses->muxlock);
346 list_for_each_entry_safe(rreq, rptr, &v9ses->mux_fcalls, next) {
347 rreq->err = err;
348 }
349 spin_unlock(&v9ses->muxlock);
350 wake_up_all(&v9ses->read_wait);
351}
352
353/**
354 * v9fs_recvproc - kproc to handle demultiplexing responses
355 * @data: session info structure
356 *
357 */
358
359static int v9fs_recvproc(void *data)
360{
361 struct v9fs_session_info *v9ses = (struct v9fs_session_info *)data;
362 struct v9fs_fcall *rcall = NULL;
363 struct v9fs_rpcreq *rptr;
364 struct v9fs_rpcreq *req;
365 struct v9fs_rpcreq *rreq;
366 int err = 0;
367
368 allow_signal(SIGKILL);
369 set_current_state(TASK_INTERRUPTIBLE);
370 complete(&v9ses->proccmpl);
371 while (!kthread_should_stop() && err >= 0) {
372 req = rptr = rreq = NULL;
373
374 rcall = kmalloc(v9ses->maxdata + V9FS_IOHDRSZ, GFP_KERNEL);
375 if (!rcall) {
376 eprintk(KERN_ERR, "no memory for buffers\n");
377 break;
378 }
379
380 err = read_message(v9ses, rcall, v9ses->maxdata + V9FS_IOHDRSZ);
381 spin_lock(&v9ses->muxlock);
382 if (err < 0) {
383 list_for_each_entry_safe(rreq, rptr, &v9ses->mux_fcalls, next) {
384 rreq->err = err;
385 }
386 if(err != -ERESTARTSYS)
387 eprintk(KERN_ERR,
388 "Transport error while reading message %d\n", err);
389 } else {
390 list_for_each_entry_safe(rreq, rptr, &v9ses->mux_fcalls, next) {
391 if (rreq->tcall->tag == rcall->tag) {
392 req = rreq;
393 req->rcall = rcall;
394 break;
395 }
396 }
397 }
398
399 if (req && (req->tcall->id == TFLUSH)) {
400 struct v9fs_rpcreq *treq = NULL;
401 list_for_each_entry_safe(treq, rptr, &v9ses->mux_fcalls, next) {
402 if (treq->tcall->tag ==
403 req->tcall->params.tflush.oldtag) {
404 list_del(&rptr->next);
405 kfree(treq->rcall);
406 break;
407 }
408 }
409 }
410
411 spin_unlock(&v9ses->muxlock);
412
413 if (!req) {
414 if (err >= 0)
415 dprintk(DEBUG_ERROR,
416 "unexpected response: id %d tag %d\n",
417 rcall->id, rcall->tag);
418
419 kfree(rcall);
420 }
421
422 wake_up_all(&v9ses->read_wait);
423 set_current_state(TASK_INTERRUPTIBLE);
424 }
425
426 v9ses->transport->close(v9ses->transport);
427
428 /* Inform all pending processes about the failure */
429 wake_up_all(&v9ses->read_wait);
430
431 if (signal_pending(current))
432 complete(&v9ses->proccmpl);
433
434 dprintk(DEBUG_MUX, "recvproc: end\n");
435 v9ses->recvproc = NULL;
436
437 return err >= 0;
438}
439
440/**
441 * v9fs_mux_init - initialize multiplexer (spawn kproc)
442 * @v9ses: session info structure
443 * @dev_name: mount device information (to create unique kproc)
444 *
445 */
446
447int v9fs_mux_init(struct v9fs_session_info *v9ses, const char *dev_name)
448{
449 char procname[60];
450
451 strncpy(procname, dev_name, sizeof(procname));
452 procname[sizeof(procname) - 1] = 0;
453
454 init_waitqueue_head(&v9ses->read_wait);
455 init_completion(&v9ses->fcread);
456 init_completion(&v9ses->proccmpl);
457 spin_lock_init(&v9ses->muxlock);
458 INIT_LIST_HEAD(&v9ses->mux_fcalls);
459 v9ses->recvproc = NULL;
460 v9ses->curfcall = NULL;
461
462 v9ses->recvproc = kthread_create(v9fs_recvproc, v9ses,
463 "v9fs_recvproc %s", procname);
464
465 if (IS_ERR(v9ses->recvproc)) {
466 eprintk(KERN_ERR, "cannot create receiving thread\n");
467 v9fs_session_close(v9ses);
468 return -ECONNABORTED;
469 }
470
471 wake_up_process(v9ses->recvproc);
472 wait_for_completion(&v9ses->proccmpl);
473
474 return 0;
475}
diff --git a/fs/9p/mux.h b/fs/9p/mux.h
new file mode 100644
index 0000000000..4994cb10ba
--- /dev/null
+++ b/fs/9p/mux.h
@@ -0,0 +1,41 @@
1/*
2 * linux/fs/9p/mux.h
3 *
4 * Multiplexer Definitions
5 *
6 * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
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/* structure to manage each RPC transaction */
27
28struct v9fs_rpcreq {
29 struct v9fs_fcall *tcall;
30 struct v9fs_fcall *rcall;
31 int err; /* error code if response failed */
32
33 /* XXX - could we put scatter/gather buffers here? */
34
35 struct list_head next;
36};
37
38int v9fs_mux_init(struct v9fs_session_info *v9ses, const char *dev_name);
39long v9fs_mux_rpc(struct v9fs_session_info *v9ses,
40 struct v9fs_fcall *tcall, struct v9fs_fcall **rcall);
41void v9fs_mux_cancel_requests(struct v9fs_session_info *v9ses, int err);
diff --git a/fs/9p/trans_fd.c b/fs/9p/trans_fd.c
new file mode 100644
index 0000000000..63b58ce98f
--- /dev/null
+++ b/fs/9p/trans_fd.c
@@ -0,0 +1,172 @@
1/*
2 * linux/fs/9p/trans_fd.c
3 *
4 * File Descriptor Transport Layer
5 *
6 * Copyright (C) 2005 by Eric Van Hensbergen <ericvh@gmail.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
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/config.h>
27#include <linux/module.h>
28#include <linux/net.h>
29#include <linux/ipv6.h>
30#include <linux/errno.h>
31#include <linux/kernel.h>
32#include <linux/un.h>
33#include <asm/uaccess.h>
34#include <linux/inet.h>
35#include <linux/idr.h>
36#include <linux/file.h>
37
38#include "debug.h"
39#include "v9fs.h"
40#include "transport.h"
41
42struct v9fs_trans_fd {
43 struct file *in_file;
44 struct file *out_file;
45};
46
47/**
48 * v9fs_fd_recv - receive from a socket
49 * @v9ses: session information
50 * @v: buffer to receive data into
51 * @len: size of receive buffer
52 *
53 */
54
55static int v9fs_fd_recv(struct v9fs_transport *trans, void *v, int len)
56{
57 struct v9fs_trans_fd *ts = trans ? trans->priv : NULL;
58
59 if (!trans || trans->status != Connected || !ts)
60 return -EIO;
61
62 return kernel_read(ts->in_file, ts->in_file->f_pos, v, len);
63}
64
65/**
66 * v9fs_fd_send - send to a socket
67 * @v9ses: session information
68 * @v: buffer to send data from
69 * @len: size of send buffer
70 *
71 */
72
73static int v9fs_fd_send(struct v9fs_transport *trans, void *v, int len)
74{
75 struct v9fs_trans_fd *ts = trans ? trans->priv : NULL;
76 mm_segment_t oldfs = get_fs();
77 int ret = 0;
78
79 if (!trans || trans->status != Connected || !ts)
80 return -EIO;
81
82 set_fs(get_ds());
83 /* The cast to a user pointer is valid due to the set_fs() */
84 ret = vfs_write(ts->out_file, (void __user *)v, len, &ts->out_file->f_pos);
85 set_fs(oldfs);
86
87 return ret;
88}
89
90/**
91 * v9fs_fd_init - initialize file descriptor transport
92 * @v9ses: session information
93 * @addr: address of server to mount
94 * @data: mount options
95 *
96 */
97
98static int
99v9fs_fd_init(struct v9fs_session_info *v9ses, const char *addr, char *data)
100{
101 struct v9fs_trans_fd *ts = NULL;
102 struct v9fs_transport *trans = v9ses->transport;
103
104 if((v9ses->wfdno == ~0) || (v9ses->rfdno == ~0)) {
105 printk(KERN_ERR "v9fs: Insufficient options for proto=fd\n");
106 return -ENOPROTOOPT;
107 }
108
109 sema_init(&trans->writelock, 1);
110 sema_init(&trans->readlock, 1);
111
112 ts = kmalloc(sizeof(struct v9fs_trans_fd), GFP_KERNEL);
113
114 if (!ts)
115 return -ENOMEM;
116
117 ts->in_file = fget( v9ses->rfdno );
118 ts->out_file = fget( v9ses->wfdno );
119
120 if (!ts->in_file || !ts->out_file) {
121 if (ts->in_file)
122 fput(ts->in_file);
123
124 if (ts->out_file)
125 fput(ts->out_file);
126
127 kfree(ts);
128 return -EIO;
129 }
130
131 trans->priv = ts;
132 trans->status = Connected;
133
134 return 0;
135}
136
137
138/**
139 * v9fs_fd_close - shutdown file descriptor
140 * @trans: private socket structure
141 *
142 */
143
144static void v9fs_fd_close(struct v9fs_transport *trans)
145{
146 struct v9fs_trans_fd *ts;
147
148 if (!trans)
149 return;
150
151 trans->status = Disconnected;
152 ts = trans->priv;
153
154 if (!ts)
155 return;
156
157 if (ts->in_file)
158 fput(ts->in_file);
159
160 if (ts->out_file)
161 fput(ts->out_file);
162
163 kfree(ts);
164}
165
166struct v9fs_transport v9fs_trans_fd = {
167 .init = v9fs_fd_init,
168 .write = v9fs_fd_send,
169 .read = v9fs_fd_recv,
170 .close = v9fs_fd_close,
171};
172
diff --git a/fs/9p/trans_sock.c b/fs/9p/trans_sock.c
new file mode 100644
index 0000000000..01e26f0013
--- /dev/null
+++ b/fs/9p/trans_sock.c
@@ -0,0 +1,290 @@
1/*
2 * linux/fs/9p/trans_socket.c
3 *
4 * Socket Transport Layer
5 *
6 * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
7 * Copyright (C) 1997-2002 by Ron Minnich <rminnich@sarnoff.com>
8 * Copyright (C) 1995, 1996 by Olaf Kirch <okir@monad.swb.de>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to:
22 * Free Software Foundation
23 * 51 Franklin Street, Fifth Floor
24 * Boston, MA 02111-1301 USA
25 *
26 */
27
28#include <linux/config.h>
29#include <linux/module.h>
30#include <linux/net.h>
31#include <linux/ipv6.h>
32#include <linux/errno.h>
33#include <linux/kernel.h>
34#include <linux/un.h>
35#include <asm/uaccess.h>
36#include <linux/inet.h>
37#include <linux/idr.h>
38
39#include "debug.h"
40#include "v9fs.h"
41#include "transport.h"
42
43#define V9FS_PORT 564
44
45struct v9fs_trans_sock {
46 struct socket *s;
47};
48
49/**
50 * v9fs_sock_recv - receive from a socket
51 * @v9ses: session information
52 * @v: buffer to receive data into
53 * @len: size of receive buffer
54 *
55 */
56
57static int v9fs_sock_recv(struct v9fs_transport *trans, void *v, int len)
58{
59 struct msghdr msg;
60 struct kvec iov;
61 int result;
62 mm_segment_t oldfs;
63 struct v9fs_trans_sock *ts = trans ? trans->priv : NULL;
64
65 if (trans->status == Disconnected)
66 return -EREMOTEIO;
67
68 result = -EINVAL;
69
70 oldfs = get_fs();
71 set_fs(get_ds());
72
73 iov.iov_base = v;
74 iov.iov_len = len;
75 msg.msg_name = NULL;
76 msg.msg_namelen = 0;
77 msg.msg_iovlen = 1;
78 msg.msg_control = NULL;
79 msg.msg_controllen = 0;
80 msg.msg_namelen = 0;
81 msg.msg_flags = MSG_NOSIGNAL;
82
83 result = kernel_recvmsg(ts->s, &msg, &iov, 1, len, 0);
84
85 dprintk(DEBUG_TRANS, "socket state %d\n", ts->s->state);
86 set_fs(oldfs);
87
88 if (result <= 0) {
89 if (result != -ERESTARTSYS)
90 trans->status = Disconnected;
91 }
92
93 return result;
94}
95
96/**
97 * v9fs_sock_send - send to a socket
98 * @v9ses: session information
99 * @v: buffer to send data from
100 * @len: size of send buffer
101 *
102 */
103
104static int v9fs_sock_send(struct v9fs_transport *trans, void *v, int len)
105{
106 struct kvec iov;
107 struct msghdr msg;
108 int result = -1;
109 mm_segment_t oldfs;
110 struct v9fs_trans_sock *ts = trans ? trans->priv : NULL;
111
112 dprintk(DEBUG_TRANS, "Sending packet size %d (%x)\n", len, len);
113 dump_data(v, len);
114
115 down(&trans->writelock);
116
117 oldfs = get_fs();
118 set_fs(get_ds());
119 iov.iov_base = v;
120 iov.iov_len = len;
121 msg.msg_name = NULL;
122 msg.msg_namelen = 0;
123 msg.msg_iovlen = 1;
124 msg.msg_control = NULL;
125 msg.msg_controllen = 0;
126 msg.msg_namelen = 0;
127 msg.msg_flags = MSG_NOSIGNAL;
128 result = kernel_sendmsg(ts->s, &msg, &iov, 1, len);
129 set_fs(oldfs);
130
131 if (result < 0) {
132 if (result != -ERESTARTSYS)
133 trans->status = Disconnected;
134 }
135
136 up(&trans->writelock);
137 return result;
138}
139
140/**
141 * v9fs_tcp_init - initialize TCP socket
142 * @v9ses: session information
143 * @addr: address of server to mount
144 * @data: mount options
145 *
146 */
147
148static int
149v9fs_tcp_init(struct v9fs_session_info *v9ses, const char *addr, char *data)
150{
151 struct socket *csocket = NULL;
152 struct sockaddr_in sin_server;
153 int rc = 0;
154 struct v9fs_trans_sock *ts = NULL;
155 struct v9fs_transport *trans = v9ses->transport;
156
157 sema_init(&trans->writelock, 1);
158 sema_init(&trans->readlock, 1);
159
160 ts = kmalloc(sizeof(struct v9fs_trans_sock), GFP_KERNEL);
161
162 if (!ts)
163 return -ENOMEM;
164
165 trans->priv = ts;
166 ts->s = NULL;
167
168 if (!addr)
169 return -EINVAL;
170
171 dprintk(DEBUG_TRANS, "Connecting to %s\n", addr);
172
173 sin_server.sin_family = AF_INET;
174 sin_server.sin_addr.s_addr = in_aton(addr);
175 sin_server.sin_port = htons(v9ses->port);
176 sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, &csocket);
177 rc = csocket->ops->connect(csocket,
178 (struct sockaddr *)&sin_server,
179 sizeof(struct sockaddr_in), 0);
180 if (rc < 0) {
181 eprintk(KERN_ERR,
182 "v9fs_trans_tcp: problem connecting socket to %s\n",
183 addr);
184 return rc;
185 }
186 csocket->sk->sk_allocation = GFP_NOIO;
187 ts->s = csocket;
188 trans->status = Connected;
189
190 return 0;
191}
192
193/**
194 * v9fs_unix_init - initialize UNIX domain socket
195 * @v9ses: session information
196 * @dev_name: path to named pipe
197 * @data: mount options
198 *
199 */
200
201static int
202v9fs_unix_init(struct v9fs_session_info *v9ses, const char *dev_name,
203 char *data)
204{
205 int rc;
206 struct socket *csocket;
207 struct sockaddr_un sun_server;
208 struct v9fs_transport *trans;
209 struct v9fs_trans_sock *ts;
210
211 rc = 0;
212 csocket = NULL;
213 trans = v9ses->transport;
214
215 if (strlen(dev_name) > UNIX_PATH_MAX) {
216 eprintk(KERN_ERR, "v9fs_trans_unix: address too long: %s\n",
217 dev_name);
218 return -ENOMEM;
219 }
220
221 ts = kmalloc(sizeof(struct v9fs_trans_sock), GFP_KERNEL);
222 if (!ts)
223 return -ENOMEM;
224
225 trans->priv = ts;
226 ts->s = NULL;
227
228 sema_init(&trans->writelock, 1);
229 sema_init(&trans->readlock, 1);
230
231 sun_server.sun_family = PF_UNIX;
232 strcpy(sun_server.sun_path, dev_name);
233 sock_create_kern(PF_UNIX, SOCK_STREAM, 0, &csocket);
234 rc = csocket->ops->connect(csocket, (struct sockaddr *)&sun_server,
235 sizeof(struct sockaddr_un) - 1, 0); /* -1 *is* important */
236 if (rc < 0) {
237 eprintk(KERN_ERR,
238 "v9fs_trans_unix: problem connecting socket: %s: %d\n",
239 dev_name, rc);
240 return rc;
241 }
242 csocket->sk->sk_allocation = GFP_NOIO;
243 ts->s = csocket;
244 trans->status = Connected;
245
246 return 0;
247}
248
249/**
250 * v9fs_sock_close - shutdown socket
251 * @trans: private socket structure
252 *
253 */
254
255static void v9fs_sock_close(struct v9fs_transport *trans)
256{
257 struct v9fs_trans_sock *ts;
258
259 if (!trans)
260 return;
261
262 ts = trans->priv;
263
264 if ((ts) && (ts->s)) {
265 dprintk(DEBUG_TRANS, "closing the socket %p\n", ts->s);
266 sock_release(ts->s);
267 ts->s = NULL;
268 trans->status = Disconnected;
269 dprintk(DEBUG_TRANS, "socket closed\n");
270 }
271
272 if (ts)
273 kfree(ts);
274
275 trans->priv = NULL;
276}
277
278struct v9fs_transport v9fs_trans_tcp = {
279 .init = v9fs_tcp_init,
280 .write = v9fs_sock_send,
281 .read = v9fs_sock_recv,
282 .close = v9fs_sock_close,
283};
284
285struct v9fs_transport v9fs_trans_unix = {
286 .init = v9fs_unix_init,
287 .write = v9fs_sock_send,
288 .read = v9fs_sock_recv,
289 .close = v9fs_sock_close,
290};
diff --git a/fs/9p/transport.h b/fs/9p/transport.h
new file mode 100644
index 0000000000..9e9cd418ef
--- /dev/null
+++ b/fs/9p/transport.h
@@ -0,0 +1,46 @@
1/*
2 * linux/fs/9p/transport.h
3 *
4 * Transport Definition
5 *
6 * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
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
26enum v9fs_transport_status {
27 Connected,
28 Disconnected,
29 Hung,
30};
31
32struct v9fs_transport {
33 enum v9fs_transport_status status;
34 struct semaphore writelock;
35 struct semaphore readlock;
36 void *priv;
37
38 int (*init) (struct v9fs_session_info *, const char *, char *);
39 int (*write) (struct v9fs_transport *, void *, int);
40 int (*read) (struct v9fs_transport *, void *, int);
41 void (*close) (struct v9fs_transport *);
42};
43
44extern struct v9fs_transport v9fs_trans_tcp;
45extern struct v9fs_transport v9fs_trans_unix;
46extern struct v9fs_transport v9fs_trans_fd;
diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c
new file mode 100644
index 0000000000..13bdbbab43
--- /dev/null
+++ b/fs/9p/v9fs.c
@@ -0,0 +1,452 @@
1/*
2 * linux/fs/9p/v9fs.c
3 *
4 * This file contains functions assisting in mapping VFS to 9P2000
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/config.h>
28#include <linux/module.h>
29#include <linux/errno.h>
30#include <linux/fs.h>
31#include <linux/parser.h>
32#include <linux/idr.h>
33
34#include "debug.h"
35#include "v9fs.h"
36#include "9p.h"
37#include "v9fs_vfs.h"
38#include "transport.h"
39#include "mux.h"
40#include "conv.h"
41
42/* TODO: sysfs or debugfs interface */
43int v9fs_debug_level = 0; /* feature-rific global debug level */
44
45/*
46 * Option Parsing (code inspired by NFS code)
47 *
48 */
49
50enum {
51 /* Options that take integer arguments */
52 Opt_port, Opt_msize, Opt_uid, Opt_gid, Opt_afid, Opt_debug,
53 Opt_rfdno, Opt_wfdno,
54 /* String options */
55 Opt_name, Opt_remotename,
56 /* Options that take no arguments */
57 Opt_legacy, Opt_nodevmap, Opt_unix, Opt_tcp, Opt_fd,
58 /* Error token */
59 Opt_err
60};
61
62static match_table_t tokens = {
63 {Opt_port, "port=%u"},
64 {Opt_msize, "msize=%u"},
65 {Opt_uid, "uid=%u"},
66 {Opt_gid, "gid=%u"},
67 {Opt_afid, "afid=%u"},
68 {Opt_rfdno, "rfdno=%u"},
69 {Opt_wfdno, "wfdno=%u"},
70 {Opt_debug, "debug=%u"},
71 {Opt_name, "name=%s"},
72 {Opt_remotename, "aname=%s"},
73 {Opt_unix, "proto=unix"},
74 {Opt_tcp, "proto=tcp"},
75 {Opt_fd, "proto=fd"},
76 {Opt_tcp, "tcp"},
77 {Opt_unix, "unix"},
78 {Opt_fd, "fd"},
79 {Opt_legacy, "noextend"},
80 {Opt_nodevmap, "nodevmap"},
81 {Opt_err, NULL}
82};
83
84/*
85 * Parse option string.
86 */
87
88/**
89 * v9fs_parse_options - parse mount options into session structure
90 * @options: options string passed from mount
91 * @v9ses: existing v9fs session information
92 *
93 */
94
95static void v9fs_parse_options(char *options, struct v9fs_session_info *v9ses)
96{
97 char *p;
98 substring_t args[MAX_OPT_ARGS];
99 int option;
100 int ret;
101
102 /* setup defaults */
103 v9ses->port = V9FS_PORT;
104 v9ses->maxdata = 9000;
105 v9ses->proto = PROTO_TCP;
106 v9ses->extended = 1;
107 v9ses->afid = ~0;
108 v9ses->debug = 0;
109 v9ses->rfdno = ~0;
110 v9ses->wfdno = ~0;
111
112 if (!options)
113 return;
114
115 while ((p = strsep(&options, ",")) != NULL) {
116 int token;
117 if (!*p)
118 continue;
119 token = match_token(p, tokens, args);
120 if (token < Opt_name) {
121 if ((ret = match_int(&args[0], &option)) < 0) {
122 dprintk(DEBUG_ERROR,
123 "integer field, but no integer?\n");
124 continue;
125 }
126
127 }
128 switch (token) {
129 case Opt_port:
130 v9ses->port = option;
131 break;
132 case Opt_msize:
133 v9ses->maxdata = option;
134 break;
135 case Opt_uid:
136 v9ses->uid = option;
137 break;
138 case Opt_gid:
139 v9ses->gid = option;
140 break;
141 case Opt_afid:
142 v9ses->afid = option;
143 break;
144 case Opt_rfdno:
145 v9ses->rfdno = option;
146 break;
147 case Opt_wfdno:
148 v9ses->wfdno = option;
149 break;
150 case Opt_debug:
151 v9ses->debug = option;
152 break;
153 case Opt_tcp:
154 v9ses->proto = PROTO_TCP;
155 break;
156 case Opt_unix:
157 v9ses->proto = PROTO_UNIX;
158 break;
159 case Opt_fd:
160 v9ses->proto = PROTO_FD;
161 break;
162 case Opt_name:
163 match_strcpy(v9ses->name, &args[0]);
164 break;
165 case Opt_remotename:
166 match_strcpy(v9ses->remotename, &args[0]);
167 break;
168 case Opt_legacy:
169 v9ses->extended = 0;
170 break;
171 case Opt_nodevmap:
172 v9ses->nodev = 1;
173 break;
174 default:
175 continue;
176 }
177 }
178}
179
180/**
181 * v9fs_inode2v9ses - safely extract v9fs session info from super block
182 * @inode: inode to extract information from
183 *
184 * Paranoid function to extract v9ses information from superblock,
185 * if anything is missing it will report an error.
186 *
187 */
188
189struct v9fs_session_info *v9fs_inode2v9ses(struct inode *inode)
190{
191 return (inode->i_sb->s_fs_info);
192}
193
194/**
195 * v9fs_get_idpool - allocate numeric id from pool
196 * @p - pool to allocate from
197 *
198 * XXX - This seems to be an awful generic function, should it be in idr.c with
199 * the lock included in struct idr?
200 */
201
202int v9fs_get_idpool(struct v9fs_idpool *p)
203{
204 int i = 0;
205 int error;
206
207retry:
208 if (idr_pre_get(&p->pool, GFP_KERNEL) == 0)
209 return 0;
210
211 if (down_interruptible(&p->lock) == -EINTR) {
212 eprintk(KERN_WARNING, "Interrupted while locking\n");
213 return -1;
214 }
215
216 error = idr_get_new(&p->pool, NULL, &i);
217 up(&p->lock);
218
219 if (error == -EAGAIN)
220 goto retry;
221 else if (error)
222 return -1;
223
224 return i;
225}
226
227/**
228 * v9fs_put_idpool - release numeric id from pool
229 * @p - pool to allocate from
230 *
231 * XXX - This seems to be an awful generic function, should it be in idr.c with
232 * the lock included in struct idr?
233 */
234
235void v9fs_put_idpool(int id, struct v9fs_idpool *p)
236{
237 if (down_interruptible(&p->lock) == -EINTR) {
238 eprintk(KERN_WARNING, "Interrupted while locking\n");
239 return;
240 }
241 idr_remove(&p->pool, id);
242 up(&p->lock);
243}
244
245/**
246 * v9fs_session_init - initialize session
247 * @v9ses: session information structure
248 * @dev_name: device being mounted
249 * @data: options
250 *
251 */
252
253int
254v9fs_session_init(struct v9fs_session_info *v9ses,
255 const char *dev_name, char *data)
256{
257 struct v9fs_fcall *fcall = NULL;
258 struct v9fs_transport *trans_proto;
259 int n = 0;
260 int newfid = -1;
261 int retval = -EINVAL;
262
263 v9ses->name = __getname();
264 if (!v9ses->name)
265 return -ENOMEM;
266
267 v9ses->remotename = __getname();
268 if (!v9ses->remotename) {
269 putname(v9ses->name);
270 return -ENOMEM;
271 }
272
273 strcpy(v9ses->name, V9FS_DEFUSER);
274 strcpy(v9ses->remotename, V9FS_DEFANAME);
275
276 v9fs_parse_options(data, v9ses);
277
278 /* set global debug level */
279 v9fs_debug_level = v9ses->debug;
280
281 /* id pools that are session-dependent: FIDs and TIDs */
282 idr_init(&v9ses->fidpool.pool);
283 init_MUTEX(&v9ses->fidpool.lock);
284 idr_init(&v9ses->tidpool.pool);
285 init_MUTEX(&v9ses->tidpool.lock);
286
287
288 switch (v9ses->proto) {
289 case PROTO_TCP:
290 trans_proto = &v9fs_trans_tcp;
291 break;
292 case PROTO_UNIX:
293 trans_proto = &v9fs_trans_unix;
294 *v9ses->remotename = 0;
295 break;
296 case PROTO_FD:
297 trans_proto = &v9fs_trans_fd;
298 *v9ses->remotename = 0;
299 break;
300 default:
301 printk(KERN_ERR "v9fs: Bad mount protocol %d\n", v9ses->proto);
302 retval = -ENOPROTOOPT;
303 goto SessCleanUp;
304 };
305
306 v9ses->transport = trans_proto;
307
308 if ((retval = v9ses->transport->init(v9ses, dev_name, data)) < 0) {
309 eprintk(KERN_ERR, "problem initializing transport\n");
310 goto SessCleanUp;
311 }
312
313 v9ses->inprogress = 0;
314 v9ses->shutdown = 0;
315 v9ses->session_hung = 0;
316
317 if ((retval = v9fs_mux_init(v9ses, dev_name)) < 0) {
318 dprintk(DEBUG_ERROR, "problem initializing mux\n");
319 goto SessCleanUp;
320 }
321
322 if (v9ses->afid == ~0) {
323 if (v9ses->extended)
324 retval =
325 v9fs_t_version(v9ses, v9ses->maxdata, "9P2000.u",
326 &fcall);
327 else
328 retval = v9fs_t_version(v9ses, v9ses->maxdata, "9P2000",
329 &fcall);
330
331 if (retval < 0) {
332 dprintk(DEBUG_ERROR, "v9fs_t_version failed\n");
333 goto FreeFcall;
334 }
335
336 /* Really should check for 9P1 and report error */
337 if (!strcmp(fcall->params.rversion.version, "9P2000.u")) {
338 dprintk(DEBUG_9P, "9P2000 UNIX extensions enabled\n");
339 v9ses->extended = 1;
340 } else {
341 dprintk(DEBUG_9P, "9P2000 legacy mode enabled\n");
342 v9ses->extended = 0;
343 }
344
345 n = fcall->params.rversion.msize;
346 kfree(fcall);
347
348 if (n < v9ses->maxdata)
349 v9ses->maxdata = n;
350 }
351
352 newfid = v9fs_get_idpool(&v9ses->fidpool);
353 if (newfid < 0) {
354 eprintk(KERN_WARNING, "couldn't allocate FID\n");
355 retval = -ENOMEM;
356 goto SessCleanUp;
357 }
358 /* it is a little bit ugly, but we have to prevent newfid */
359 /* being the same as afid, so if it is, get a new fid */
360 if (v9ses->afid != ~0 && newfid == v9ses->afid) {
361 newfid = v9fs_get_idpool(&v9ses->fidpool);
362 if (newfid < 0) {
363 eprintk(KERN_WARNING, "couldn't allocate FID\n");
364 retval = -ENOMEM;
365 goto SessCleanUp;
366 }
367 }
368
369 if ((retval =
370 v9fs_t_attach(v9ses, v9ses->name, v9ses->remotename, newfid,
371 v9ses->afid, NULL))
372 < 0) {
373 dprintk(DEBUG_ERROR, "cannot attach\n");
374 goto SessCleanUp;
375 }
376
377 if (v9ses->afid != ~0) {
378 if (v9fs_t_clunk(v9ses, v9ses->afid, NULL))
379 dprintk(DEBUG_ERROR, "clunk failed\n");
380 }
381
382 return newfid;
383
384 FreeFcall:
385 kfree(fcall);
386
387 SessCleanUp:
388 v9fs_session_close(v9ses);
389 return retval;
390}
391
392/**
393 * v9fs_session_close - shutdown a session
394 * @v9ses: session information structure
395 *
396 */
397
398void v9fs_session_close(struct v9fs_session_info *v9ses)
399{
400 if (v9ses->recvproc) {
401 send_sig(SIGKILL, v9ses->recvproc, 1);
402 wait_for_completion(&v9ses->proccmpl);
403 }
404
405 if (v9ses->transport)
406 v9ses->transport->close(v9ses->transport);
407
408 putname(v9ses->name);
409 putname(v9ses->remotename);
410}
411
412/**
413 * v9fs_session_cancel - mark transport as disconnected
414 * and cancel all pending requests.
415 */
416void v9fs_session_cancel(struct v9fs_session_info *v9ses) {
417 v9ses->transport->status = Disconnected;
418 v9fs_mux_cancel_requests(v9ses, -EIO);
419}
420
421extern int v9fs_error_init(void);
422
423/**
424 * v9fs_init - Initialize module
425 *
426 */
427
428static int __init init_v9fs(void)
429{
430 v9fs_error_init();
431
432 printk(KERN_INFO "Installing v9fs 9P2000 file system support\n");
433
434 return register_filesystem(&v9fs_fs_type);
435}
436
437/**
438 * v9fs_init - shutdown module
439 *
440 */
441
442static void __exit exit_v9fs(void)
443{
444 unregister_filesystem(&v9fs_fs_type);
445}
446
447module_init(init_v9fs)
448module_exit(exit_v9fs)
449
450MODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>");
451MODULE_AUTHOR("Ron Minnich <rminnich@lanl.gov>");
452MODULE_LICENSE("GPL");
diff --git a/fs/9p/v9fs.h b/fs/9p/v9fs.h
new file mode 100644
index 0000000000..45dcef42bd
--- /dev/null
+++ b/fs/9p/v9fs.h
@@ -0,0 +1,103 @@
1/*
2 * V9FS definitions.
3 *
4 * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
5 * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to:
19 * Free Software Foundation
20 * 51 Franklin Street, Fifth Floor
21 * Boston, MA 02111-1301 USA
22 *
23 */
24
25/*
26 * Idpool structure provides lock and id management
27 *
28 */
29
30struct v9fs_idpool {
31 struct semaphore lock;
32 struct idr pool;
33};
34
35/*
36 * Session structure provides information for an opened session
37 *
38 */
39
40struct v9fs_session_info {
41 /* options */
42 unsigned int maxdata;
43 unsigned char extended; /* set to 1 if we are using UNIX extensions */
44 unsigned char nodev; /* set to 1 if no disable device mapping */
45 unsigned short port; /* port to connect to */
46 unsigned short debug; /* debug level */
47 unsigned short proto; /* protocol to use */
48 unsigned int afid; /* authentication fid */
49 unsigned int rfdno; /* read file descriptor number */
50 unsigned int wfdno; /* write file descriptor number */
51
52
53 char *name; /* user name to mount as */
54 char *remotename; /* name of remote hierarchy being mounted */
55 unsigned int uid; /* default uid/muid for legacy support */
56 unsigned int gid; /* default gid for legacy support */
57
58 /* book keeping */
59 struct v9fs_idpool fidpool; /* The FID pool for file descriptors */
60 struct v9fs_idpool tidpool; /* The TID pool for transactions ids */
61
62 /* transport information */
63 struct v9fs_transport *transport;
64
65 int inprogress; /* session in progress => true */
66 int shutdown; /* session shutting down. no more attaches. */
67 unsigned char session_hung;
68
69 /* mux private data */
70 struct v9fs_fcall *curfcall;
71 wait_queue_head_t read_wait;
72 struct completion fcread;
73 struct completion proccmpl;
74 struct task_struct *recvproc;
75
76 spinlock_t muxlock;
77 struct list_head mux_fcalls;
78};
79
80/* possible values of ->proto */
81enum {
82 PROTO_TCP,
83 PROTO_UNIX,
84 PROTO_FD,
85};
86
87int v9fs_session_init(struct v9fs_session_info *, const char *, char *);
88struct v9fs_session_info *v9fs_inode2v9ses(struct inode *);
89void v9fs_session_close(struct v9fs_session_info *v9ses);
90int v9fs_get_idpool(struct v9fs_idpool *p);
91void v9fs_put_idpool(int id, struct v9fs_idpool *p);
92void v9fs_session_cancel(struct v9fs_session_info *v9ses);
93
94#define V9FS_MAGIC 0x01021997
95
96/* other default globals */
97#define V9FS_PORT 564
98#define V9FS_DEFUSER "nobody"
99#define V9FS_DEFANAME ""
100
101/* inital pool sizes for fids and tags */
102#define V9FS_START_FIDS 8192
103#define V9FS_START_TIDS 256
diff --git a/fs/9p/v9fs_vfs.h b/fs/9p/v9fs_vfs.h
new file mode 100644
index 0000000000..2f2cea7ee3
--- /dev/null
+++ b/fs/9p/v9fs_vfs.h
@@ -0,0 +1,53 @@
1/*
2 * V9FS VFS extensions.
3 *
4 * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
5 * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to:
19 * Free Software Foundation
20 * 51 Franklin Street, Fifth Floor
21 * Boston, MA 02111-1301 USA
22 *
23 */
24
25/* plan9 semantics are that created files are implicitly opened.
26 * But linux semantics are that you call create, then open.
27 * the plan9 approach is superior as it provides an atomic
28 * open.
29 * we track the create fid here. When the file is opened, if fidopen is
30 * non-zero, we use the fid and can skip some steps.
31 * there may be a better way to do this, but I don't know it.
32 * one BAD way is to clunk the fid on create, then open it again:
33 * you lose the atomicity of file open
34 */
35
36/* special case:
37 * unlink calls remove, which is an implicit clunk. So we have to track
38 * that kind of thing so that we don't try to clunk a dead fid.
39 */
40
41extern struct file_system_type v9fs_fs_type;
42extern struct file_operations v9fs_file_operations;
43extern struct file_operations v9fs_dir_operations;
44extern struct dentry_operations v9fs_dentry_operations;
45
46struct inode *v9fs_get_inode(struct super_block *sb, int mode);
47ino_t v9fs_qid2ino(struct v9fs_qid *qid);
48void v9fs_mistat2inode(struct v9fs_stat *, struct inode *,
49 struct super_block *);
50int v9fs_dir_release(struct inode *inode, struct file *filp);
51int v9fs_file_open(struct inode *inode, struct file *file);
52void v9fs_inode2mistat(struct inode *inode, struct v9fs_stat *mistat);
53void v9fs_dentry_release(struct dentry *);
diff --git a/fs/9p/vfs_dentry.c b/fs/9p/vfs_dentry.c
new file mode 100644
index 0000000000..306c96741f
--- /dev/null
+++ b/fs/9p/vfs_dentry.c
@@ -0,0 +1,126 @@
1/*
2 * linux/fs/9p/vfs_dentry.c
3 *
4 * This file contians vfs dentry 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/pagemap.h>
32#include <linux/stat.h>
33#include <linux/string.h>
34#include <linux/smp_lock.h>
35#include <linux/inet.h>
36#include <linux/namei.h>
37#include <linux/idr.h>
38
39#include "debug.h"
40#include "v9fs.h"
41#include "9p.h"
42#include "v9fs_vfs.h"
43#include "conv.h"
44#include "fid.h"
45
46/**
47 * v9fs_dentry_validate - VFS dcache hook to validate cache
48 * @dentry: dentry that is being validated
49 * @nd: path data
50 *
51 * dcache really shouldn't be used for 9P2000 as at all due to
52 * potential attached semantics to directory traversal (walk).
53 *
54 * FUTURE: look into how to use dcache to allow multi-stage
55 * walks in Plan 9 & potential for better dcache operation which
56 * would remain valid for Plan 9 semantics. Older versions
57 * had validation via stat for those interested. However, since
58 * stat has the same approximate overhead as walk there really
59 * is no difference. The only improvement would be from a
60 * time-decay cache like NFS has and that undermines the
61 * synchronous nature of 9P2000.
62 *
63 */
64
65static int v9fs_dentry_validate(struct dentry *dentry, struct nameidata *nd)
66{
67 struct dentry *dc = current->fs->pwd;
68
69 dprintk(DEBUG_VFS, "dentry: %s (%p)\n", dentry->d_iname, dentry);
70 if (v9fs_fid_lookup(dentry, FID_OP)) {
71 dprintk(DEBUG_VFS, "VALID\n");
72 return 1;
73 }
74
75 while (dc != NULL) {
76 if (dc == dentry) {
77 dprintk(DEBUG_VFS, "VALID\n");
78 return 1;
79 }
80 if (dc == dc->d_parent)
81 break;
82
83 dc = dc->d_parent;
84 }
85
86 dprintk(DEBUG_VFS, "INVALID\n");
87 return 0;
88}
89
90/**
91 * v9fs_dentry_release - called when dentry is going to be freed
92 * @dentry: dentry that is being release
93 *
94 */
95
96void v9fs_dentry_release(struct dentry *dentry)
97{
98 dprintk(DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry);
99
100 if (dentry->d_fsdata != NULL) {
101 struct list_head *fid_list = dentry->d_fsdata;
102 struct v9fs_fid *temp = NULL;
103 struct v9fs_fid *current_fid = NULL;
104 struct v9fs_fcall *fcall = NULL;
105
106 list_for_each_entry_safe(current_fid, temp, fid_list, list) {
107 if (v9fs_t_clunk
108 (current_fid->v9ses, current_fid->fid, &fcall))
109 dprintk(DEBUG_ERROR, "clunk failed: %s\n",
110 FCALL_ERROR(fcall));
111
112 v9fs_put_idpool(current_fid->fid,
113 &current_fid->v9ses->fidpool);
114
115 kfree(fcall);
116 v9fs_fid_destroy(current_fid);
117 }
118
119 kfree(dentry->d_fsdata); /* free the list_head */
120 }
121}
122
123struct dentry_operations v9fs_dentry_operations = {
124 .d_revalidate = v9fs_dentry_validate,
125 .d_release = v9fs_dentry_release,
126};
diff --git a/fs/9p/vfs_dir.c b/fs/9p/vfs_dir.c
new file mode 100644
index 0000000000..c478a73841
--- /dev/null
+++ b/fs/9p/vfs_dir.c
@@ -0,0 +1,226 @@
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 "v9fs_vfs.h"
41#include "conv.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;
78 int fid = -1;
79 int ret = 0;
80 struct v9fs_stat *mi = NULL;
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 mi = kmalloc(v9ses->maxdata, GFP_KERNEL);
88 if (!mi)
89 return -ENOMEM;
90
91 if (file->rdir_fcall && (filp->f_pos != file->rdir_pos)) {
92 kfree(file->rdir_fcall);
93 file->rdir_fcall = NULL;
94 }
95
96 if (file->rdir_fcall) {
97 n = file->rdir_fcall->params.rread.count;
98 i = file->rdir_fpos;
99 while (i < n) {
100 int s = v9fs_deserialize_stat(v9ses,
101 file->rdir_fcall->params.rread.data + i,
102 n - i, mi, v9ses->maxdata);
103
104 if (s == 0) {
105 dprintk(DEBUG_ERROR,
106 "error while deserializing mistat\n");
107 ret = -EIO;
108 goto FreeStructs;
109 }
110
111 over = filldir(dirent, mi->name, strlen(mi->name),
112 filp->f_pos, v9fs_qid2ino(&mi->qid),
113 dt_type(mi));
114
115 if (over) {
116 file->rdir_fpos = i;
117 file->rdir_pos = filp->f_pos;
118 break;
119 }
120
121 i += s;
122 filp->f_pos += s;
123 }
124
125 if (!over) {
126 kfree(file->rdir_fcall);
127 file->rdir_fcall = NULL;
128 }
129 }
130
131 while (!over) {
132 ret = v9fs_t_read(v9ses, fid, filp->f_pos,
133 v9ses->maxdata-V9FS_IOHDRSZ, &fcall);
134 if (ret < 0) {
135 dprintk(DEBUG_ERROR, "error while reading: %d: %p\n",
136 ret, fcall);
137 goto FreeStructs;
138 } else if (ret == 0)
139 break;
140
141 n = ret;
142 i = 0;
143 while (i < n) {
144 int s = v9fs_deserialize_stat(v9ses,
145 fcall->params.rread.data + i, n - i, mi,
146 v9ses->maxdata);
147
148 if (s == 0) {
149 dprintk(DEBUG_ERROR,
150 "error while deserializing mistat\n");
151 return -EIO;
152 }
153
154 over = filldir(dirent, mi->name, strlen(mi->name),
155 filp->f_pos, v9fs_qid2ino(&mi->qid),
156 dt_type(mi));
157
158 if (over) {
159 file->rdir_fcall = fcall;
160 file->rdir_fpos = i;
161 file->rdir_pos = filp->f_pos;
162 fcall = NULL;
163 break;
164 }
165
166 i += s;
167 filp->f_pos += s;
168 }
169
170 kfree(fcall);
171 }
172
173 FreeStructs:
174 kfree(fcall);
175 kfree(mi);
176 return ret;
177}
178
179/**
180 * v9fs_dir_release - close a directory
181 * @inode: inode of the directory
182 * @filp: file pointer to a directory
183 *
184 */
185
186int v9fs_dir_release(struct inode *inode, struct file *filp)
187{
188 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode);
189 struct v9fs_fid *fid = filp->private_data;
190 int fidnum = -1;
191
192 dprintk(DEBUG_VFS, "inode: %p filp: %p fid: %d\n", inode, filp,
193 fid->fid);
194 fidnum = fid->fid;
195
196 filemap_fdatawrite(inode->i_mapping);
197 filemap_fdatawait(inode->i_mapping);
198
199 if (fidnum >= 0) {
200 fid->fidopen--;
201 dprintk(DEBUG_VFS, "fidopen: %d v9f->fid: %d\n", fid->fidopen,
202 fid->fid);
203
204 if (fid->fidopen == 0) {
205 if (v9fs_t_clunk(v9ses, fidnum, NULL))
206 dprintk(DEBUG_ERROR, "clunk failed\n");
207
208 v9fs_put_idpool(fid->fid, &v9ses->fidpool);
209 }
210
211 kfree(fid->rdir_fcall);
212
213 filp->private_data = NULL;
214 v9fs_fid_destroy(fid);
215 }
216
217 d_drop(filp->f_dentry);
218 return 0;
219}
220
221struct file_operations v9fs_dir_operations = {
222 .read = generic_read_dir,
223 .readdir = v9fs_dir_readdir,
224 .open = v9fs_file_open,
225 .release = v9fs_dir_release,
226};
diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c
new file mode 100644
index 0000000000..1f8ae7d580
--- /dev/null
+++ b/fs/9p/vfs_file.c
@@ -0,0 +1,401 @@
1/*
2 * linux/fs/9p/vfs_file.c
3 *
4 * This file contians vfs file ops for 9P2000.
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/version.h>
36#include <linux/list.h>
37#include <asm/uaccess.h>
38#include <linux/idr.h>
39
40#include "debug.h"
41#include "v9fs.h"
42#include "9p.h"
43#include "v9fs_vfs.h"
44#include "fid.h"
45
46/**
47 * v9fs_file_open - open a file (or directory)
48 * @inode: inode to be opened
49 * @file: file being opened
50 *
51 */
52
53int v9fs_file_open(struct inode *inode, struct file *file)
54{
55 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode);
56 struct v9fs_fid *v9fid = v9fs_fid_lookup(file->f_dentry, FID_WALK);
57 struct v9fs_fid *v9newfid = NULL;
58 struct v9fs_fcall *fcall = NULL;
59 int open_mode = 0;
60 unsigned int iounit = 0;
61 int newfid = -1;
62 long result = -1;
63
64 dprintk(DEBUG_VFS, "inode: %p file: %p v9fid= %p\n", inode, file,
65 v9fid);
66
67 if (!v9fid) {
68 struct dentry *dentry = file->f_dentry;
69 dprintk(DEBUG_ERROR, "Couldn't resolve fid from dentry\n");
70
71 /* XXX - some duplication from lookup, generalize later */
72 /* basically vfs_lookup is too heavy weight */
73 v9fid = v9fs_fid_lookup(file->f_dentry, FID_OP);
74 if (!v9fid)
75 return -EBADF;
76
77 v9fid = v9fs_fid_lookup(dentry->d_parent, FID_WALK);
78 if (!v9fid)
79 return -EBADF;
80
81 newfid = v9fs_get_idpool(&v9ses->fidpool);
82 if (newfid < 0) {
83 eprintk(KERN_WARNING, "newfid fails!\n");
84 return -ENOSPC;
85 }
86
87 result =
88 v9fs_t_walk(v9ses, v9fid->fid, newfid,
89 (char *)file->f_dentry->d_name.name, NULL);
90 if (result < 0) {
91 v9fs_put_idpool(newfid, &v9ses->fidpool);
92 dprintk(DEBUG_ERROR, "rewalk didn't work\n");
93 return -EBADF;
94 }
95
96 v9fid = v9fs_fid_create(dentry);
97 if (v9fid == NULL) {
98 dprintk(DEBUG_ERROR, "couldn't insert\n");
99 return -ENOMEM;
100 }
101 v9fid->fid = newfid;
102 }
103
104 if (v9fid->fidcreate) {
105 /* create case */
106 newfid = v9fid->fid;
107 iounit = v9fid->iounit;
108 v9fid->fidcreate = 0;
109 } else {
110 if (!S_ISDIR(inode->i_mode))
111 newfid = v9fid->fid;
112 else {
113 newfid = v9fs_get_idpool(&v9ses->fidpool);
114 if (newfid < 0) {
115 eprintk(KERN_WARNING, "allocation failed\n");
116 return -ENOSPC;
117 }
118 /* This would be a somewhat critical clone */
119 result =
120 v9fs_t_walk(v9ses, v9fid->fid, newfid, NULL,
121 &fcall);
122 if (result < 0) {
123 dprintk(DEBUG_ERROR, "clone error: %s\n",
124 FCALL_ERROR(fcall));
125 kfree(fcall);
126 return result;
127 }
128
129 v9newfid = v9fs_fid_create(file->f_dentry);
130 v9newfid->fid = newfid;
131 v9newfid->qid = v9fid->qid;
132 v9newfid->iounit = v9fid->iounit;
133 v9newfid->fidopen = 0;
134 v9newfid->fidclunked = 0;
135 v9newfid->v9ses = v9ses;
136 v9fid = v9newfid;
137 kfree(fcall);
138 }
139
140 /* TODO: do special things for O_EXCL, O_NOFOLLOW, O_SYNC */
141 /* translate open mode appropriately */
142 open_mode = file->f_flags & 0x3;
143
144 if (file->f_flags & O_EXCL)
145 open_mode |= V9FS_OEXCL;
146
147 if (v9ses->extended) {
148 if (file->f_flags & O_TRUNC)
149 open_mode |= V9FS_OTRUNC;
150
151 if (file->f_flags & O_APPEND)
152 open_mode |= V9FS_OAPPEND;
153 }
154
155 result = v9fs_t_open(v9ses, newfid, open_mode, &fcall);
156 if (result < 0) {
157 dprintk(DEBUG_ERROR,
158 "open failed, open_mode 0x%x: %s\n", open_mode,
159 FCALL_ERROR(fcall));
160 kfree(fcall);
161 return result;
162 }
163
164 iounit = fcall->params.ropen.iounit;
165 kfree(fcall);
166 }
167
168
169 file->private_data = v9fid;
170
171 v9fid->rdir_pos = 0;
172 v9fid->rdir_fcall = NULL;
173 v9fid->fidopen = 1;
174 v9fid->filp = file;
175 v9fid->iounit = iounit;
176
177 return 0;
178}
179
180/**
181 * v9fs_file_lock - lock a file (or directory)
182 * @inode: inode to be opened
183 * @file: file being opened
184 *
185 * XXX - this looks like a local only lock, we should extend into 9P
186 * by using open exclusive
187 */
188
189static int v9fs_file_lock(struct file *filp, int cmd, struct file_lock *fl)
190{
191 int res = 0;
192 struct inode *inode = filp->f_dentry->d_inode;
193
194 dprintk(DEBUG_VFS, "filp: %p lock: %p\n", filp, fl);
195
196 /* No mandatory locks */
197 if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
198 return -ENOLCK;
199
200 if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {
201 filemap_fdatawrite(inode->i_mapping);
202 filemap_fdatawait(inode->i_mapping);
203 invalidate_inode_pages(&inode->i_data);
204 }
205
206 return res;
207}
208
209/**
210 * v9fs_read - read from a file (internal)
211 * @filep: file pointer to read
212 * @data: data buffer to read data into
213 * @count: size of buffer
214 * @offset: offset at which to read data
215 *
216 */
217
218static ssize_t
219v9fs_read(struct file *filp, char *buffer, size_t count, loff_t * offset)
220{
221 struct inode *inode = filp->f_dentry->d_inode;
222 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode);
223 struct v9fs_fid *v9f = filp->private_data;
224 struct v9fs_fcall *fcall = NULL;
225 int fid = v9f->fid;
226 int rsize = 0;
227 int result = 0;
228 int total = 0;
229
230 dprintk(DEBUG_VFS, "\n");
231
232 rsize = v9ses->maxdata - V9FS_IOHDRSZ;
233 if (v9f->iounit != 0 && rsize > v9f->iounit)
234 rsize = v9f->iounit;
235
236 do {
237 if (count < rsize)
238 rsize = count;
239
240 result = v9fs_t_read(v9ses, fid, *offset, rsize, &fcall);
241
242 if (result < 0) {
243 printk(KERN_ERR "9P2000: v9fs_t_read returned %d\n",
244 result);
245
246 kfree(fcall);
247 return total;
248 } else
249 *offset += result;
250
251 /* XXX - extra copy */
252 memcpy(buffer, fcall->params.rread.data, result);
253 count -= result;
254 buffer += result;
255 total += result;
256
257 kfree(fcall);
258
259 if (result < rsize)
260 break;
261 } while (count);
262
263 return total;
264}
265
266/**
267 * v9fs_file_read - read from a file
268 * @filep: file pointer to read
269 * @data: data buffer to read data into
270 * @count: size of buffer
271 * @offset: offset at which to read data
272 *
273 */
274
275static ssize_t
276v9fs_file_read(struct file *filp, char __user * data, size_t count,
277 loff_t * offset)
278{
279 int retval = -1;
280 int ret = 0;
281 char *buffer;
282
283 buffer = kmalloc(count, GFP_KERNEL);
284 if (!buffer)
285 return -ENOMEM;
286
287 retval = v9fs_read(filp, buffer, count, offset);
288 if (retval > 0) {
289 if ((ret = copy_to_user(data, buffer, retval)) != 0) {
290 dprintk(DEBUG_ERROR, "Problem copying to user %d\n",
291 ret);
292 retval = ret;
293 }
294 }
295
296 kfree(buffer);
297
298 return retval;
299}
300
301/**
302 * v9fs_write - write to a file
303 * @filep: file pointer to write
304 * @data: data buffer to write data from
305 * @count: size of buffer
306 * @offset: offset at which to write data
307 *
308 */
309
310static ssize_t
311v9fs_write(struct file *filp, char *buffer, size_t count, loff_t * offset)
312{
313 struct inode *inode = filp->f_dentry->d_inode;
314 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode);
315 struct v9fs_fid *v9fid = filp->private_data;
316 struct v9fs_fcall *fcall;
317 int fid = v9fid->fid;
318 int result = -EIO;
319 int rsize = 0;
320 int total = 0;
321
322 dprintk(DEBUG_VFS, "data %p count %d offset %x\n", buffer, (int)count,
323 (int)*offset);
324 rsize = v9ses->maxdata - V9FS_IOHDRSZ;
325 if (v9fid->iounit != 0 && rsize > v9fid->iounit)
326 rsize = v9fid->iounit;
327
328 dump_data(buffer, count);
329
330 do {
331 if (count < rsize)
332 rsize = count;
333
334 result =
335 v9fs_t_write(v9ses, fid, *offset, rsize, buffer, &fcall);
336 if (result < 0) {
337 eprintk(KERN_ERR, "error while writing: %s(%d)\n",
338 FCALL_ERROR(fcall), result);
339 kfree(fcall);
340 return result;
341 } else
342 *offset += result;
343
344 kfree(fcall);
345
346 if (result != rsize) {
347 eprintk(KERN_ERR,
348 "short write: v9fs_t_write returned %d\n",
349 result);
350 break;
351 }
352
353 count -= result;
354 buffer += result;
355 total += result;
356 } while (count);
357
358 return total;
359}
360
361/**
362 * v9fs_file_write - write to a file
363 * @filep: file pointer to write
364 * @data: data buffer to write data from
365 * @count: size of buffer
366 * @offset: offset at which to write data
367 *
368 */
369
370static ssize_t
371v9fs_file_write(struct file *filp, const char __user * data,
372 size_t count, loff_t * offset)
373{
374 int ret = -1;
375 char *buffer;
376
377 buffer = kmalloc(count, GFP_KERNEL);
378 if (buffer == NULL)
379 return -ENOMEM;
380
381 ret = copy_from_user(buffer, data, count);
382 if (ret) {
383 dprintk(DEBUG_ERROR, "Problem copying from user\n");
384 ret = -EFAULT;
385 } else {
386 ret = v9fs_write(filp, buffer, count, offset);
387 }
388
389 kfree(buffer);
390
391 return ret;
392}
393
394struct file_operations v9fs_file_operations = {
395 .llseek = generic_file_llseek,
396 .read = v9fs_file_read,
397 .write = v9fs_file_write,
398 .open = v9fs_file_open,
399 .release = v9fs_dir_release,
400 .lock = v9fs_file_lock,
401};
diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c
new file mode 100644
index 0000000000..0c13fc6000
--- /dev/null
+++ b/fs/9p/vfs_inode.c
@@ -0,0 +1,1338 @@
1/*
2 * linux/fs/9p/vfs_inode.c
3 *
4 * This file contains vfs inode 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/pagemap.h>
32#include <linux/stat.h>
33#include <linux/string.h>
34#include <linux/smp_lock.h>
35#include <linux/inet.h>
36#include <linux/namei.h>
37#include <linux/idr.h>
38
39#include "debug.h"
40#include "v9fs.h"
41#include "9p.h"
42#include "v9fs_vfs.h"
43#include "conv.h"
44#include "fid.h"
45
46static struct inode_operations v9fs_dir_inode_operations;
47static struct inode_operations v9fs_dir_inode_operations_ext;
48static struct inode_operations v9fs_file_inode_operations;
49static struct inode_operations v9fs_symlink_inode_operations;
50
51/**
52 * unixmode2p9mode - convert unix mode bits to plan 9
53 * @v9ses: v9fs session information
54 * @mode: mode to convert
55 *
56 */
57
58static int unixmode2p9mode(struct v9fs_session_info *v9ses, int mode)
59{
60 int res;
61 res = mode & 0777;
62 if (S_ISDIR(mode))
63 res |= V9FS_DMDIR;
64 if (v9ses->extended) {
65 if (S_ISLNK(mode))
66 res |= V9FS_DMSYMLINK;
67 if (v9ses->nodev == 0) {
68 if (S_ISSOCK(mode))
69 res |= V9FS_DMSOCKET;
70 if (S_ISFIFO(mode))
71 res |= V9FS_DMNAMEDPIPE;
72 if (S_ISBLK(mode))
73 res |= V9FS_DMDEVICE;
74 if (S_ISCHR(mode))
75 res |= V9FS_DMDEVICE;
76 }
77
78 if ((mode & S_ISUID) == S_ISUID)
79 res |= V9FS_DMSETUID;
80 if ((mode & S_ISGID) == S_ISGID)
81 res |= V9FS_DMSETGID;
82 if ((mode & V9FS_DMLINK))
83 res |= V9FS_DMLINK;
84 }
85
86 return res;
87}
88
89/**
90 * p9mode2unixmode- convert plan9 mode bits to unix mode bits
91 * @v9ses: v9fs session information
92 * @mode: mode to convert
93 *
94 */
95
96static int p9mode2unixmode(struct v9fs_session_info *v9ses, int mode)
97{
98 int res;
99
100 res = mode & 0777;
101
102 if ((mode & V9FS_DMDIR) == V9FS_DMDIR)
103 res |= S_IFDIR;
104 else if ((mode & V9FS_DMSYMLINK) && (v9ses->extended))
105 res |= S_IFLNK;
106 else if ((mode & V9FS_DMSOCKET) && (v9ses->extended)
107 && (v9ses->nodev == 0))
108 res |= S_IFSOCK;
109 else if ((mode & V9FS_DMNAMEDPIPE) && (v9ses->extended)
110 && (v9ses->nodev == 0))
111 res |= S_IFIFO;
112 else if ((mode & V9FS_DMDEVICE) && (v9ses->extended)
113 && (v9ses->nodev == 0))
114 res |= S_IFBLK;
115 else
116 res |= S_IFREG;
117
118 if (v9ses->extended) {
119 if ((mode & V9FS_DMSETUID) == V9FS_DMSETUID)
120 res |= S_ISUID;
121
122 if ((mode & V9FS_DMSETGID) == V9FS_DMSETGID)
123 res |= S_ISGID;
124 }
125
126 return res;
127}
128
129/**
130 * v9fs_blank_mistat - helper function to setup a 9P stat structure
131 * @v9ses: 9P session info (for determining extended mode)
132 * @mistat: structure to initialize
133 *
134 */
135
136static void
137v9fs_blank_mistat(struct v9fs_session_info *v9ses, struct v9fs_stat *mistat)
138{
139 mistat->type = ~0;
140 mistat->dev = ~0;
141 mistat->qid.type = ~0;
142 mistat->qid.version = ~0;
143 *((long long *)&mistat->qid.path) = ~0;
144 mistat->mode = ~0;
145 mistat->atime = ~0;
146 mistat->mtime = ~0;
147 mistat->length = ~0;
148 mistat->name = mistat->data;
149 mistat->uid = mistat->data;
150 mistat->gid = mistat->data;
151 mistat->muid = mistat->data;
152 if (v9ses->extended) {
153 mistat->n_uid = ~0;
154 mistat->n_gid = ~0;
155 mistat->n_muid = ~0;
156 mistat->extension = mistat->data;
157 }
158 *mistat->data = 0;
159}
160
161/**
162 * v9fs_mistat2unix - convert mistat to unix stat
163 * @mistat: Plan 9 metadata (mistat) structure
164 * @buf: unix metadata (stat) structure to populate
165 * @sb: superblock
166 *
167 */
168
169static void
170v9fs_mistat2unix(struct v9fs_stat *mistat, struct stat *buf,
171 struct super_block *sb)
172{
173 struct v9fs_session_info *v9ses = sb ? sb->s_fs_info : NULL;
174
175 buf->st_nlink = 1;
176
177 buf->st_atime = mistat->atime;
178 buf->st_mtime = mistat->mtime;
179 buf->st_ctime = mistat->mtime;
180
181 buf->st_uid = (unsigned short)-1;
182 buf->st_gid = (unsigned short)-1;
183
184 if (v9ses && v9ses->extended) {
185 /* TODO: string to uid mapping via user-space daemon */
186 if (mistat->n_uid != -1)
187 sscanf(mistat->uid, "%x", (unsigned int *)&buf->st_uid);
188
189 if (mistat->n_gid != -1)
190 sscanf(mistat->gid, "%x", (unsigned int *)&buf->st_gid);
191 }
192
193 if (buf->st_uid == (unsigned short)-1)
194 buf->st_uid = v9ses->uid;
195 if (buf->st_gid == (unsigned short)-1)
196 buf->st_gid = v9ses->gid;
197
198 buf->st_mode = p9mode2unixmode(v9ses, mistat->mode);
199 if ((S_ISBLK(buf->st_mode)) || (S_ISCHR(buf->st_mode))) {
200 char type = 0;
201 int major = -1;
202 int minor = -1;
203 sscanf(mistat->extension, "%c %u %u", &type, &major, &minor);
204 switch (type) {
205 case 'c':
206 buf->st_mode &= ~S_IFBLK;
207 buf->st_mode |= S_IFCHR;
208 break;
209 case 'b':
210 break;
211 default:
212 dprintk(DEBUG_ERROR, "Unknown special type %c (%s)\n",
213 type, mistat->extension);
214 };
215 buf->st_rdev = MKDEV(major, minor);
216 } else
217 buf->st_rdev = 0;
218
219 buf->st_size = mistat->length;
220
221 buf->st_blksize = sb->s_blocksize;
222 buf->st_blocks =
223 (buf->st_size + buf->st_blksize - 1) >> sb->s_blocksize_bits;
224}
225
226/**
227 * v9fs_get_inode - helper function to setup an inode
228 * @sb: superblock
229 * @mode: mode to setup inode with
230 *
231 */
232
233struct inode *v9fs_get_inode(struct super_block *sb, int mode)
234{
235 struct inode *inode = NULL;
236 struct v9fs_session_info *v9ses = sb->s_fs_info;
237
238 dprintk(DEBUG_VFS, "super block: %p mode: %o\n", sb, mode);
239
240 inode = new_inode(sb);
241 if (inode) {
242 inode->i_mode = mode;
243 inode->i_uid = current->fsuid;
244 inode->i_gid = current->fsgid;
245 inode->i_blksize = sb->s_blocksize;
246 inode->i_blocks = 0;
247 inode->i_rdev = 0;
248 inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
249
250 switch (mode & S_IFMT) {
251 case S_IFIFO:
252 case S_IFBLK:
253 case S_IFCHR:
254 case S_IFSOCK:
255 if(!v9ses->extended) {
256 dprintk(DEBUG_ERROR, "special files without extended mode\n");
257 return ERR_PTR(-EINVAL);
258 }
259 init_special_inode(inode, inode->i_mode,
260 inode->i_rdev);
261 break;
262 case S_IFREG:
263 inode->i_op = &v9fs_file_inode_operations;
264 inode->i_fop = &v9fs_file_operations;
265 break;
266 case S_IFLNK:
267 if(!v9ses->extended) {
268 dprintk(DEBUG_ERROR, "extended modes used w/o 9P2000.u\n");
269 return ERR_PTR(-EINVAL);
270 }
271 inode->i_op = &v9fs_symlink_inode_operations;
272 break;
273 case S_IFDIR:
274 inode->i_nlink++;
275 if(v9ses->extended)
276 inode->i_op = &v9fs_dir_inode_operations_ext;
277 else
278 inode->i_op = &v9fs_dir_inode_operations;
279 inode->i_fop = &v9fs_dir_operations;
280 break;
281 default:
282 dprintk(DEBUG_ERROR, "BAD mode 0x%x S_IFMT 0x%x\n",
283 mode, mode & S_IFMT);
284 return ERR_PTR(-EINVAL);
285 }
286 } else {
287 eprintk(KERN_WARNING, "Problem allocating inode\n");
288 return ERR_PTR(-ENOMEM);
289 }
290 return inode;
291}
292
293/**
294 * v9fs_create - helper function to create files and directories
295 * @dir: directory inode file is being created in
296 * @file_dentry: dentry file is being created in
297 * @perm: permissions file is being created with
298 * @open_mode: resulting open mode for file
299 *
300 */
301
302static int
303v9fs_create(struct inode *dir,
304 struct dentry *file_dentry,
305 unsigned int perm, unsigned int open_mode)
306{
307 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir);
308 struct super_block *sb = dir->i_sb;
309 struct v9fs_fid *dirfid =
310 v9fs_fid_lookup(file_dentry->d_parent, FID_WALK);
311 struct v9fs_fid *fid = NULL;
312 struct inode *file_inode = NULL;
313 struct v9fs_fcall *fcall = NULL;
314 struct v9fs_qid qid;
315 struct stat newstat;
316 int dirfidnum = -1;
317 long newfid = -1;
318 int result = 0;
319 unsigned int iounit = 0;
320
321 perm = unixmode2p9mode(v9ses, perm);
322
323 dprintk(DEBUG_VFS, "dir: %p dentry: %p perm: %o mode: %o\n", dir,
324 file_dentry, perm, open_mode);
325
326 if (!dirfid)
327 return -EBADF;
328
329 dirfidnum = dirfid->fid;
330 if (dirfidnum < 0) {
331 dprintk(DEBUG_ERROR, "No fid for the directory #%lu\n",
332 dir->i_ino);
333 return -EBADF;
334 }
335
336 if (file_dentry->d_inode) {
337 dprintk(DEBUG_ERROR,
338 "Odd. There is an inode for dir %lu, name :%s:\n",
339 dir->i_ino, file_dentry->d_name.name);
340 return -EEXIST;
341 }
342
343 newfid = v9fs_get_idpool(&v9ses->fidpool);
344 if (newfid < 0) {
345 eprintk(KERN_WARNING, "no free fids available\n");
346 return -ENOSPC;
347 }
348
349 result = v9fs_t_walk(v9ses, dirfidnum, newfid, NULL, &fcall);
350 if (result < 0) {
351 dprintk(DEBUG_ERROR, "clone error: %s\n", FCALL_ERROR(fcall));
352 v9fs_put_idpool(newfid, &v9ses->fidpool);
353 newfid = 0;
354 goto CleanUpFid;
355 }
356
357 kfree(fcall);
358
359 result = v9fs_t_create(v9ses, newfid, (char *)file_dentry->d_name.name,
360 perm, open_mode, &fcall);
361 if (result < 0) {
362 dprintk(DEBUG_ERROR, "create fails: %s(%d)\n",
363 FCALL_ERROR(fcall), result);
364
365 goto CleanUpFid;
366 }
367
368 iounit = fcall->params.rcreate.iounit;
369 qid = fcall->params.rcreate.qid;
370 kfree(fcall);
371
372 fid = v9fs_fid_create(file_dentry);
373 if (!fid) {
374 result = -ENOMEM;
375 goto CleanUpFid;
376 }
377
378 fid->fid = newfid;
379 fid->fidopen = 0;
380 fid->fidcreate = 1;
381 fid->qid = qid;
382 fid->iounit = iounit;
383 fid->rdir_pos = 0;
384 fid->rdir_fcall = NULL;
385 fid->v9ses = v9ses;
386
387 if ((perm & V9FS_DMSYMLINK) || (perm & V9FS_DMLINK) ||
388 (perm & V9FS_DMNAMEDPIPE) || (perm & V9FS_DMSOCKET) ||
389 (perm & V9FS_DMDEVICE))
390 return 0;
391
392 result = v9fs_t_stat(v9ses, newfid, &fcall);
393 if (result < 0) {
394 dprintk(DEBUG_ERROR, "stat error: %s(%d)\n", FCALL_ERROR(fcall),
395 result);
396 goto CleanUpFid;
397 }
398
399 v9fs_mistat2unix(fcall->params.rstat.stat, &newstat, sb);
400
401 file_inode = v9fs_get_inode(sb, newstat.st_mode);
402 if ((!file_inode) || IS_ERR(file_inode)) {
403 dprintk(DEBUG_ERROR, "create inode failed\n");
404 result = -EBADF;
405 goto CleanUpFid;
406 }
407
408 v9fs_mistat2inode(fcall->params.rstat.stat, file_inode, sb);
409 kfree(fcall);
410 d_instantiate(file_dentry, file_inode);
411
412 if (perm & V9FS_DMDIR) {
413 if (v9fs_t_clunk(v9ses, newfid, &fcall))
414 dprintk(DEBUG_ERROR, "clunk for mkdir failed: %s\n",
415 FCALL_ERROR(fcall));
416
417 v9fs_put_idpool(newfid, &v9ses->fidpool);
418 kfree(fcall);
419 fid->fidopen = 0;
420 fid->fidcreate = 0;
421 d_drop(file_dentry);
422 }
423
424 return 0;
425
426 CleanUpFid:
427 kfree(fcall);
428
429 if (newfid) {
430 if (v9fs_t_clunk(v9ses, newfid, &fcall))
431 dprintk(DEBUG_ERROR, "clunk failed: %s\n",
432 FCALL_ERROR(fcall));
433
434 v9fs_put_idpool(newfid, &v9ses->fidpool);
435 kfree(fcall);
436 }
437 return result;
438}
439
440/**
441 * v9fs_remove - helper function to remove files and directories
442 * @dir: directory inode that is being deleted
443 * @file: dentry that is being deleted
444 * @rmdir: removing a directory
445 *
446 */
447
448static int v9fs_remove(struct inode *dir, struct dentry *file, int rmdir)
449{
450 struct v9fs_fcall *fcall = NULL;
451 struct super_block *sb = NULL;
452 struct v9fs_session_info *v9ses = NULL;
453 struct v9fs_fid *v9fid = NULL;
454 struct inode *file_inode = NULL;
455 int fid = -1;
456 int result = 0;
457
458 dprintk(DEBUG_VFS, "inode: %p dentry: %p rmdir: %d\n", dir, file,
459 rmdir);
460
461 file_inode = file->d_inode;
462 sb = file_inode->i_sb;
463 v9ses = v9fs_inode2v9ses(file_inode);
464 v9fid = v9fs_fid_lookup(file, FID_OP);
465
466 if (!v9fid) {
467 dprintk(DEBUG_ERROR,
468 "no v9fs_fid\n");
469 return -EBADF;
470 }
471
472 fid = v9fid->fid;
473 if (fid < 0) {
474 dprintk(DEBUG_ERROR, "inode #%lu, no fid!\n",
475 file_inode->i_ino);
476 return -EBADF;
477 }
478
479 result = v9fs_t_remove(v9ses, fid, &fcall);
480 if (result < 0)
481 dprintk(DEBUG_ERROR, "remove of file fails: %s(%d)\n",
482 FCALL_ERROR(fcall), result);
483 else {
484 v9fs_put_idpool(fid, &v9ses->fidpool);
485 v9fs_fid_destroy(v9fid);
486 }
487
488 kfree(fcall);
489 return result;
490}
491
492/**
493 * v9fs_vfs_create - VFS hook to create files
494 * @inode: directory inode that is being deleted
495 * @dentry: dentry that is being deleted
496 * @perm: create permissions
497 * @nd: path information
498 *
499 */
500
501static int
502v9fs_vfs_create(struct inode *inode, struct dentry *dentry, int perm,
503 struct nameidata *nd)
504{
505 return v9fs_create(inode, dentry, perm, O_RDWR);
506}
507
508/**
509 * v9fs_vfs_mkdir - VFS mkdir hook to create a directory
510 * @inode: inode that is being unlinked
511 * @dentry: dentry that is being unlinked
512 * @mode: mode for new directory
513 *
514 */
515
516static int v9fs_vfs_mkdir(struct inode *inode, struct dentry *dentry, int mode)
517{
518 return v9fs_create(inode, dentry, mode | S_IFDIR, O_RDONLY);
519}
520
521/**
522 * v9fs_vfs_lookup - VFS lookup hook to "walk" to a new inode
523 * @dir: inode that is being walked from
524 * @dentry: dentry that is being walked to?
525 * @nameidata: path data
526 *
527 */
528
529static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
530 struct nameidata *nameidata)
531{
532 struct super_block *sb;
533 struct v9fs_session_info *v9ses;
534 struct v9fs_fid *dirfid;
535 struct v9fs_fid *fid;
536 struct inode *inode;
537 struct v9fs_fcall *fcall = NULL;
538 struct stat newstat;
539 int dirfidnum = -1;
540 int newfid = -1;
541 int result = 0;
542
543 dprintk(DEBUG_VFS, "dir: %p dentry: (%s) %p nameidata: %p\n",
544 dir, dentry->d_iname, dentry, nameidata);
545
546 sb = dir->i_sb;
547 v9ses = v9fs_inode2v9ses(dir);
548 dirfid = v9fs_fid_lookup(dentry->d_parent, FID_WALK);
549
550 if (!dirfid) {
551 dprintk(DEBUG_ERROR, "no dirfid\n");
552 return ERR_PTR(-EINVAL);
553 }
554
555 dirfidnum = dirfid->fid;
556
557 if (dirfidnum < 0) {
558 dprintk(DEBUG_ERROR, "no dirfid for inode %p, #%lu\n",
559 dir, dir->i_ino);
560 return ERR_PTR(-EBADF);
561 }
562
563 newfid = v9fs_get_idpool(&v9ses->fidpool);
564 if (newfid < 0) {
565 eprintk(KERN_WARNING, "newfid fails!\n");
566 return ERR_PTR(-ENOSPC);
567 }
568
569 result =
570 v9fs_t_walk(v9ses, dirfidnum, newfid, (char *)dentry->d_name.name,
571 NULL);
572 if (result < 0) {
573 v9fs_put_idpool(newfid, &v9ses->fidpool);
574 if (result == -ENOENT) {
575 d_add(dentry, NULL);
576 dprintk(DEBUG_ERROR,
577 "Return negative dentry %p count %d\n",
578 dentry, atomic_read(&dentry->d_count));
579 return NULL;
580 }
581 dprintk(DEBUG_ERROR, "walk error:%d\n", result);
582 goto FreeFcall;
583 }
584
585 result = v9fs_t_stat(v9ses, newfid, &fcall);
586 if (result < 0) {
587 dprintk(DEBUG_ERROR, "stat error\n");
588 goto FreeFcall;
589 }
590
591 v9fs_mistat2unix(fcall->params.rstat.stat, &newstat, sb);
592 inode = v9fs_get_inode(sb, newstat.st_mode);
593
594 if (IS_ERR(inode) && (PTR_ERR(inode) == -ENOSPC)) {
595 eprintk(KERN_WARNING, "inode alloc failes, returns %ld\n",
596 PTR_ERR(inode));
597
598 result = -ENOSPC;
599 goto FreeFcall;
600 }
601
602 inode->i_ino = v9fs_qid2ino(&fcall->params.rstat.stat->qid);
603
604 fid = v9fs_fid_create(dentry);
605 if (fid == NULL) {
606 dprintk(DEBUG_ERROR, "couldn't insert\n");
607 result = -ENOMEM;
608 goto FreeFcall;
609 }
610
611 fid->fid = newfid;
612 fid->fidopen = 0;
613 fid->v9ses = v9ses;
614 fid->qid = fcall->params.rstat.stat->qid;
615
616 dentry->d_op = &v9fs_dentry_operations;
617 v9fs_mistat2inode(fcall->params.rstat.stat, inode, inode->i_sb);
618
619 d_add(dentry, inode);
620 kfree(fcall);
621
622 return NULL;
623
624 FreeFcall:
625 kfree(fcall);
626 return ERR_PTR(result);
627}
628
629/**
630 * v9fs_vfs_unlink - VFS unlink hook to delete an inode
631 * @i: inode that is being unlinked
632 * @d: dentry that is being unlinked
633 *
634 */
635
636static int v9fs_vfs_unlink(struct inode *i, struct dentry *d)
637{
638 return v9fs_remove(i, d, 0);
639}
640
641/**
642 * v9fs_vfs_rmdir - VFS unlink hook to delete a directory
643 * @i: inode that is being unlinked
644 * @d: dentry that is being unlinked
645 *
646 */
647
648static int v9fs_vfs_rmdir(struct inode *i, struct dentry *d)
649{
650 return v9fs_remove(i, d, 1);
651}
652
653/**
654 * v9fs_vfs_rename - VFS hook to rename an inode
655 * @old_dir: old dir inode
656 * @old_dentry: old dentry
657 * @new_dir: new dir inode
658 * @new_dentry: new dentry
659 *
660 */
661
662static int
663v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
664 struct inode *new_dir, struct dentry *new_dentry)
665{
666 struct inode *old_inode = old_dentry->d_inode;
667 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(old_inode);
668 struct v9fs_fid *oldfid = v9fs_fid_lookup(old_dentry, FID_WALK);
669 struct v9fs_fid *olddirfid =
670 v9fs_fid_lookup(old_dentry->d_parent, FID_WALK);
671 struct v9fs_fid *newdirfid =
672 v9fs_fid_lookup(new_dentry->d_parent, FID_WALK);
673 struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL);
674 struct v9fs_fcall *fcall = NULL;
675 int fid = -1;
676 int olddirfidnum = -1;
677 int newdirfidnum = -1;
678 int retval = 0;
679
680 dprintk(DEBUG_VFS, "\n");
681
682 if (!mistat)
683 return -ENOMEM;
684
685 if ((!oldfid) || (!olddirfid) || (!newdirfid)) {
686 dprintk(DEBUG_ERROR, "problem with arguments\n");
687 return -EBADF;
688 }
689
690 /* 9P can only handle file rename in the same directory */
691 if (memcmp(&olddirfid->qid, &newdirfid->qid, sizeof(newdirfid->qid))) {
692 dprintk(DEBUG_ERROR, "old dir and new dir are different\n");
693 retval = -EPERM;
694 goto FreeFcallnBail;
695 }
696
697 fid = oldfid->fid;
698 olddirfidnum = olddirfid->fid;
699 newdirfidnum = newdirfid->fid;
700
701 if (fid < 0) {
702 dprintk(DEBUG_ERROR, "no fid for old file #%lu\n",
703 old_inode->i_ino);
704 retval = -EBADF;
705 goto FreeFcallnBail;
706 }
707
708 v9fs_blank_mistat(v9ses, mistat);
709
710 strcpy(mistat->data + 1, v9ses->name);
711 mistat->name = mistat->data + 1 + strlen(v9ses->name);
712
713 if (new_dentry->d_name.len >
714 (v9ses->maxdata - strlen(v9ses->name) - sizeof(struct v9fs_stat))) {
715 dprintk(DEBUG_ERROR, "new name too long\n");
716 goto FreeFcallnBail;
717 }
718
719 strcpy(mistat->name, new_dentry->d_name.name);
720 retval = v9fs_t_wstat(v9ses, fid, mistat, &fcall);
721
722 FreeFcallnBail:
723 kfree(mistat);
724
725 if (retval < 0)
726 dprintk(DEBUG_ERROR, "v9fs_t_wstat error: %s\n",
727 FCALL_ERROR(fcall));
728
729 kfree(fcall);
730 return retval;
731}
732
733/**
734 * v9fs_vfs_getattr - retreive file metadata
735 * @mnt - mount information
736 * @dentry - file to get attributes on
737 * @stat - metadata structure to populate
738 *
739 */
740
741static int
742v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry,
743 struct kstat *stat)
744{
745 struct v9fs_fcall *fcall = NULL;
746 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode);
747 struct v9fs_fid *fid = v9fs_fid_lookup(dentry, FID_OP);
748 int err = -EPERM;
749
750 dprintk(DEBUG_VFS, "dentry: %p\n", dentry);
751 if (!fid) {
752 dprintk(DEBUG_ERROR,
753 "couldn't find fid associated with dentry\n");
754 return -EBADF;
755 }
756
757 err = v9fs_t_stat(v9ses, fid->fid, &fcall);
758
759 if (err < 0)
760 dprintk(DEBUG_ERROR, "stat error\n");
761 else {
762 v9fs_mistat2inode(fcall->params.rstat.stat, dentry->d_inode,
763 dentry->d_inode->i_sb);
764 generic_fillattr(dentry->d_inode, stat);
765 }
766
767 kfree(fcall);
768 return err;
769}
770
771/**
772 * v9fs_vfs_setattr - set file metadata
773 * @dentry: file whose metadata to set
774 * @iattr: metadata assignment structure
775 *
776 */
777
778static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
779{
780 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode);
781 struct v9fs_fid *fid = v9fs_fid_lookup(dentry, FID_OP);
782 struct v9fs_fcall *fcall = NULL;
783 struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL);
784 int res = -EPERM;
785
786 dprintk(DEBUG_VFS, "\n");
787
788 if (!mistat)
789 return -ENOMEM;
790
791 if (!fid) {
792 dprintk(DEBUG_ERROR,
793 "Couldn't find fid associated with dentry\n");
794 return -EBADF;
795 }
796
797 v9fs_blank_mistat(v9ses, mistat);
798 if (iattr->ia_valid & ATTR_MODE)
799 mistat->mode = unixmode2p9mode(v9ses, iattr->ia_mode);
800
801 if (iattr->ia_valid & ATTR_MTIME)
802 mistat->mtime = iattr->ia_mtime.tv_sec;
803
804 if (iattr->ia_valid & ATTR_ATIME)
805 mistat->atime = iattr->ia_atime.tv_sec;
806
807 if (iattr->ia_valid & ATTR_SIZE)
808 mistat->length = iattr->ia_size;
809
810 if (v9ses->extended) {
811 char *ptr = mistat->data+1;
812
813 if (iattr->ia_valid & ATTR_UID) {
814 mistat->uid = ptr;
815 ptr += 1+sprintf(ptr, "%08x", iattr->ia_uid);
816 mistat->n_uid = iattr->ia_uid;
817 }
818
819 if (iattr->ia_valid & ATTR_GID) {
820 mistat->gid = ptr;
821 ptr += 1+sprintf(ptr, "%08x", iattr->ia_gid);
822 mistat->n_gid = iattr->ia_gid;
823 }
824 }
825
826 res = v9fs_t_wstat(v9ses, fid->fid, mistat, &fcall);
827
828 if (res < 0)
829 dprintk(DEBUG_ERROR, "wstat error: %s\n", FCALL_ERROR(fcall));
830
831 kfree(mistat);
832 kfree(fcall);
833
834 if (res >= 0)
835 res = inode_setattr(dentry->d_inode, iattr);
836
837 return res;
838}
839
840/**
841 * v9fs_mistat2inode - populate an inode structure with mistat info
842 * @mistat: Plan 9 metadata (mistat) structure
843 * @inode: inode to populate
844 * @sb: superblock of filesystem
845 *
846 */
847
848void
849v9fs_mistat2inode(struct v9fs_stat *mistat, struct inode *inode,
850 struct super_block *sb)
851{
852 struct v9fs_session_info *v9ses = sb->s_fs_info;
853
854 inode->i_nlink = 1;
855
856 inode->i_atime.tv_sec = mistat->atime;
857 inode->i_mtime.tv_sec = mistat->mtime;
858 inode->i_ctime.tv_sec = mistat->mtime;
859
860 inode->i_uid = -1;
861 inode->i_gid = -1;
862
863 if (v9ses->extended) {
864 /* TODO: string to uid mapping via user-space daemon */
865 inode->i_uid = mistat->n_uid;
866 inode->i_gid = mistat->n_gid;
867
868 if (mistat->n_uid == -1)
869 sscanf(mistat->uid, "%x", &inode->i_uid);
870
871 if (mistat->n_gid == -1)
872 sscanf(mistat->gid, "%x", &inode->i_gid);
873 }
874
875 if (inode->i_uid == -1)
876 inode->i_uid = v9ses->uid;
877 if (inode->i_gid == -1)
878 inode->i_gid = v9ses->gid;
879
880 inode->i_mode = p9mode2unixmode(v9ses, mistat->mode);
881 if ((S_ISBLK(inode->i_mode)) || (S_ISCHR(inode->i_mode))) {
882 char type = 0;
883 int major = -1;
884 int minor = -1;
885 sscanf(mistat->extension, "%c %u %u", &type, &major, &minor);
886 switch (type) {
887 case 'c':
888 inode->i_mode &= ~S_IFBLK;
889 inode->i_mode |= S_IFCHR;
890 break;
891 case 'b':
892 break;
893 default:
894 dprintk(DEBUG_ERROR, "Unknown special type %c (%s)\n",
895 type, mistat->extension);
896 };
897 inode->i_rdev = MKDEV(major, minor);
898 } else
899 inode->i_rdev = 0;
900
901 inode->i_size = mistat->length;
902
903 inode->i_blksize = sb->s_blocksize;
904 inode->i_blocks =
905 (inode->i_size + inode->i_blksize - 1) >> sb->s_blocksize_bits;
906}
907
908/**
909 * v9fs_qid2ino - convert qid into inode number
910 * @qid: qid to hash
911 *
912 * BUG: potential for inode number collisions?
913 */
914
915ino_t v9fs_qid2ino(struct v9fs_qid *qid)
916{
917 u64 path = qid->path + 2;
918 ino_t i = 0;
919
920 if (sizeof(ino_t) == sizeof(path))
921 memcpy(&i, &path, sizeof(ino_t));
922 else
923 i = (ino_t) (path ^ (path >> 32));
924
925 return i;
926}
927
928/**
929 * v9fs_vfs_symlink - helper function to create symlinks
930 * @dir: directory inode containing symlink
931 * @dentry: dentry for symlink
932 * @symname: symlink data
933 *
934 * See 9P2000.u RFC for more information
935 *
936 */
937
938static int
939v9fs_vfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
940{
941 int retval = -EPERM;
942 struct v9fs_fid *newfid;
943 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir);
944 struct v9fs_fcall *fcall = NULL;
945 struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL);
946
947 dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name,
948 symname);
949
950 if (!mistat)
951 return -ENOMEM;
952
953 if (!v9ses->extended) {
954 dprintk(DEBUG_ERROR, "not extended\n");
955 goto FreeFcall;
956 }
957
958 /* issue a create */
959 retval = v9fs_create(dir, dentry, S_IFLNK, 0);
960 if (retval != 0)
961 goto FreeFcall;
962
963 newfid = v9fs_fid_lookup(dentry, FID_OP);
964
965 /* issue a twstat */
966 v9fs_blank_mistat(v9ses, mistat);
967 strcpy(mistat->data + 1, symname);
968 mistat->extension = mistat->data + 1;
969 retval = v9fs_t_wstat(v9ses, newfid->fid, mistat, &fcall);
970 if (retval < 0) {
971 dprintk(DEBUG_ERROR, "v9fs_t_wstat error: %s\n",
972 FCALL_ERROR(fcall));
973 goto FreeFcall;
974 }
975
976 kfree(fcall);
977
978 if (v9fs_t_clunk(v9ses, newfid->fid, &fcall)) {
979 dprintk(DEBUG_ERROR, "clunk for symlink failed: %s\n",
980 FCALL_ERROR(fcall));
981 goto FreeFcall;
982 }
983
984 d_drop(dentry); /* FID - will this also clunk? */
985
986 FreeFcall:
987 kfree(mistat);
988 kfree(fcall);
989
990 return retval;
991}
992
993/**
994 * v9fs_readlink - read a symlink's location (internal version)
995 * @dentry: dentry for symlink
996 * @buffer: buffer to load symlink location into
997 * @buflen: length of buffer
998 *
999 */
1000
1001static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen)
1002{
1003 int retval = -EPERM;
1004
1005 struct v9fs_fcall *fcall = NULL;
1006 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dentry->d_inode);
1007 struct v9fs_fid *fid = v9fs_fid_lookup(dentry, FID_OP);
1008
1009 if (!fid) {
1010 dprintk(DEBUG_ERROR, "could not resolve fid from dentry\n");
1011 retval = -EBADF;
1012 goto FreeFcall;
1013 }
1014
1015 if (!v9ses->extended) {
1016 retval = -EBADF;
1017 dprintk(DEBUG_ERROR, "not extended\n");
1018 goto FreeFcall;
1019 }
1020
1021 dprintk(DEBUG_VFS, " %s\n", dentry->d_name.name);
1022 retval = v9fs_t_stat(v9ses, fid->fid, &fcall);
1023
1024 if (retval < 0) {
1025 dprintk(DEBUG_ERROR, "stat error\n");
1026 goto FreeFcall;
1027 }
1028
1029 if (!fcall)
1030 return -EIO;
1031
1032 if (!(fcall->params.rstat.stat->mode & V9FS_DMSYMLINK)) {
1033 retval = -EINVAL;
1034 goto FreeFcall;
1035 }
1036
1037 /* copy extension buffer into buffer */
1038 if (strlen(fcall->params.rstat.stat->extension) < buflen)
1039 buflen = strlen(fcall->params.rstat.stat->extension);
1040
1041 memcpy(buffer, fcall->params.rstat.stat->extension, buflen + 1);
1042
1043 retval = buflen;
1044
1045 FreeFcall:
1046 kfree(fcall);
1047
1048 return retval;
1049}
1050
1051/**
1052 * v9fs_vfs_readlink - read a symlink's location
1053 * @dentry: dentry for symlink
1054 * @buf: buffer to load symlink location into
1055 * @buflen: length of buffer
1056 *
1057 */
1058
1059static int v9fs_vfs_readlink(struct dentry *dentry, char __user * buffer,
1060 int buflen)
1061{
1062 int retval;
1063 int ret;
1064 char *link = __getname();
1065
1066 if (strlen(link) < buflen)
1067 buflen = strlen(link);
1068
1069 dprintk(DEBUG_VFS, " dentry: %s (%p)\n", dentry->d_iname, dentry);
1070
1071 retval = v9fs_readlink(dentry, link, buflen);
1072
1073 if (retval > 0) {
1074 if ((ret = copy_to_user(buffer, link, retval)) != 0) {
1075 dprintk(DEBUG_ERROR, "problem copying to user: %d\n",
1076 ret);
1077 retval = ret;
1078 }
1079 }
1080
1081 putname(link);
1082 return retval;
1083}
1084
1085/**
1086 * v9fs_vfs_follow_link - follow a symlink path
1087 * @dentry: dentry for symlink
1088 * @nd: nameidata
1089 *
1090 */
1091
1092static void *v9fs_vfs_follow_link(struct dentry *dentry, struct nameidata *nd)
1093{
1094 int len = 0;
1095 char *link = __getname();
1096
1097 dprintk(DEBUG_VFS, "%s n", dentry->d_name.name);
1098
1099 if (!link)
1100 link = ERR_PTR(-ENOMEM);
1101 else {
1102 len = v9fs_readlink(dentry, link, strlen(link));
1103
1104 if (len < 0) {
1105 putname(link);
1106 link = ERR_PTR(len);
1107 } else
1108 link[len] = 0;
1109 }
1110 nd_set_link(nd, link);
1111
1112 return NULL;
1113}
1114
1115/**
1116 * v9fs_vfs_put_link - release a symlink path
1117 * @dentry: dentry for symlink
1118 * @nd: nameidata
1119 *
1120 */
1121
1122static void v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, void *p)
1123{
1124 char *s = nd_get_link(nd);
1125
1126 dprintk(DEBUG_VFS, " %s %s\n", dentry->d_name.name, s);
1127 if (!IS_ERR(s))
1128 putname(s);
1129}
1130
1131/**
1132 * v9fs_vfs_link - create a hardlink
1133 * @old_dentry: dentry for file to link to
1134 * @dir: inode destination for new link
1135 * @dentry: dentry for link
1136 *
1137 */
1138
1139/* XXX - lots of code dup'd from symlink and creates,
1140 * figure out a better reuse strategy
1141 */
1142
1143static int
1144v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir,
1145 struct dentry *dentry)
1146{
1147 int retval = -EPERM;
1148 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir);
1149 struct v9fs_fcall *fcall = NULL;
1150 struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL);
1151 struct v9fs_fid *oldfid = v9fs_fid_lookup(old_dentry, FID_OP);
1152 struct v9fs_fid *newfid = NULL;
1153 char *symname = __getname();
1154
1155 dprintk(DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, dentry->d_name.name,
1156 old_dentry->d_name.name);
1157
1158 if (!v9ses->extended) {
1159 dprintk(DEBUG_ERROR, "not extended\n");
1160 goto FreeMem;
1161 }
1162
1163 /* get fid of old_dentry */
1164 sprintf(symname, "hardlink(%d)\n", oldfid->fid);
1165
1166 /* issue a create */
1167 retval = v9fs_create(dir, dentry, V9FS_DMLINK, 0);
1168 if (retval != 0)
1169 goto FreeMem;
1170
1171 newfid = v9fs_fid_lookup(dentry, FID_OP);
1172 if (!newfid) {
1173 dprintk(DEBUG_ERROR, "couldn't resolve fid from dentry\n");
1174 goto FreeMem;
1175 }
1176
1177 /* issue a twstat */
1178 v9fs_blank_mistat(v9ses, mistat);
1179 strcpy(mistat->data + 1, symname);
1180 mistat->extension = mistat->data + 1;
1181 retval = v9fs_t_wstat(v9ses, newfid->fid, mistat, &fcall);
1182 if (retval < 0) {
1183 dprintk(DEBUG_ERROR, "v9fs_t_wstat error: %s\n",
1184 FCALL_ERROR(fcall));
1185 goto FreeMem;
1186 }
1187
1188 kfree(fcall);
1189
1190 if (v9fs_t_clunk(v9ses, newfid->fid, &fcall)) {
1191 dprintk(DEBUG_ERROR, "clunk for symlink failed: %s\n",
1192 FCALL_ERROR(fcall));
1193 goto FreeMem;
1194 }
1195
1196 d_drop(dentry); /* FID - will this also clunk? */
1197
1198 kfree(fcall);
1199 fcall = NULL;
1200
1201 FreeMem:
1202 kfree(mistat);
1203 kfree(fcall);
1204 putname(symname);
1205 return retval;
1206}
1207
1208/**
1209 * v9fs_vfs_mknod - create a special file
1210 * @dir: inode destination for new link
1211 * @dentry: dentry for file
1212 * @mode: mode for creation
1213 * @dev_t: device associated with special file
1214 *
1215 */
1216
1217static int
1218v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
1219{
1220 int retval = -EPERM;
1221 struct v9fs_fid *newfid;
1222 struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir);
1223 struct v9fs_fcall *fcall = NULL;
1224 struct v9fs_stat *mistat = kmalloc(v9ses->maxdata, GFP_KERNEL);
1225 char *symname = __getname();
1226
1227 dprintk(DEBUG_VFS, " %lu,%s mode: %x MAJOR: %u MINOR: %u\n", dir->i_ino,
1228 dentry->d_name.name, mode, MAJOR(rdev), MINOR(rdev));
1229
1230 if (!mistat)
1231 return -ENOMEM;
1232
1233 if (!new_valid_dev(rdev)) {
1234 retval = -EINVAL;
1235 goto FreeMem;
1236 }
1237
1238 if (!v9ses->extended) {
1239 dprintk(DEBUG_ERROR, "not extended\n");
1240 goto FreeMem;
1241 }
1242
1243 /* issue a create */
1244 retval = v9fs_create(dir, dentry, mode, 0);
1245
1246 if (retval != 0)
1247 goto FreeMem;
1248
1249 newfid = v9fs_fid_lookup(dentry, FID_OP);
1250 if (!newfid) {
1251 dprintk(DEBUG_ERROR, "coudn't resove fid from dentry\n");
1252 retval = -EINVAL;
1253 goto FreeMem;
1254 }
1255
1256 /* build extension */
1257 if (S_ISBLK(mode))
1258 sprintf(symname, "b %u %u", MAJOR(rdev), MINOR(rdev));
1259 else if (S_ISCHR(mode))
1260 sprintf(symname, "c %u %u", MAJOR(rdev), MINOR(rdev));
1261 else if (S_ISFIFO(mode))
1262 ; /* DO NOTHING */
1263 else {
1264 retval = -EINVAL;
1265 goto FreeMem;
1266 }
1267
1268 if (!S_ISFIFO(mode)) {
1269 /* issue a twstat */
1270 v9fs_blank_mistat(v9ses, mistat);
1271 strcpy(mistat->data + 1, symname);
1272 mistat->extension = mistat->data + 1;
1273 retval = v9fs_t_wstat(v9ses, newfid->fid, mistat, &fcall);
1274 if (retval < 0) {
1275 dprintk(DEBUG_ERROR, "v9fs_t_wstat error: %s\n",
1276 FCALL_ERROR(fcall));
1277 goto FreeMem;
1278 }
1279 }
1280
1281 /* need to update dcache so we show up */
1282 kfree(fcall);
1283
1284 if (v9fs_t_clunk(v9ses, newfid->fid, &fcall)) {
1285 dprintk(DEBUG_ERROR, "clunk for symlink failed: %s\n",
1286 FCALL_ERROR(fcall));
1287 goto FreeMem;
1288 }
1289
1290 d_drop(dentry); /* FID - will this also clunk? */
1291
1292 FreeMem:
1293 kfree(mistat);
1294 kfree(fcall);
1295 putname(symname);
1296
1297 return retval;
1298}
1299
1300static struct inode_operations v9fs_dir_inode_operations_ext = {
1301 .create = v9fs_vfs_create,
1302 .lookup = v9fs_vfs_lookup,
1303 .symlink = v9fs_vfs_symlink,
1304 .link = v9fs_vfs_link,
1305 .unlink = v9fs_vfs_unlink,
1306 .mkdir = v9fs_vfs_mkdir,
1307 .rmdir = v9fs_vfs_rmdir,
1308 .mknod = v9fs_vfs_mknod,
1309 .rename = v9fs_vfs_rename,
1310 .readlink = v9fs_vfs_readlink,
1311 .getattr = v9fs_vfs_getattr,
1312 .setattr = v9fs_vfs_setattr,
1313};
1314
1315static struct inode_operations v9fs_dir_inode_operations = {
1316 .create = v9fs_vfs_create,
1317 .lookup = v9fs_vfs_lookup,
1318 .unlink = v9fs_vfs_unlink,
1319 .mkdir = v9fs_vfs_mkdir,
1320 .rmdir = v9fs_vfs_rmdir,
1321 .mknod = v9fs_vfs_mknod,
1322 .rename = v9fs_vfs_rename,
1323 .getattr = v9fs_vfs_getattr,
1324 .setattr = v9fs_vfs_setattr,
1325};
1326
1327static struct inode_operations v9fs_file_inode_operations = {
1328 .getattr = v9fs_vfs_getattr,
1329 .setattr = v9fs_vfs_setattr,
1330};
1331
1332static struct inode_operations v9fs_symlink_inode_operations = {
1333 .readlink = v9fs_vfs_readlink,
1334 .follow_link = v9fs_vfs_follow_link,
1335 .put_link = v9fs_vfs_put_link,
1336 .getattr = v9fs_vfs_getattr,
1337 .setattr = v9fs_vfs_setattr,
1338};
diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c
new file mode 100644
index 0000000000..868f350b2c
--- /dev/null
+++ b/fs/9p/vfs_super.c
@@ -0,0 +1,280 @@
1/*
2 * linux/fs/9p/vfs_super.c
3 *
4 * This file contians superblock ops for 9P2000. It is intended that
5 * you mount this file system on directories.
6 *
7 * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
8 * Copyright (C) 2002 by Ron Minnich <rminnich@lanl.gov>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to:
22 * Free Software Foundation
23 * 51 Franklin Street, Fifth Floor
24 * Boston, MA 02111-1301 USA
25 *
26 */
27
28#include <linux/kernel.h>
29#include <linux/config.h>
30#include <linux/module.h>
31#include <linux/errno.h>
32#include <linux/fs.h>
33#include <linux/file.h>
34#include <linux/stat.h>
35#include <linux/string.h>
36#include <linux/smp_lock.h>
37#include <linux/inet.h>
38#include <linux/pagemap.h>
39#include <linux/seq_file.h>
40#include <linux/mount.h>
41#include <linux/idr.h>
42
43#include "debug.h"
44#include "v9fs.h"
45#include "9p.h"
46#include "v9fs_vfs.h"
47#include "conv.h"
48#include "fid.h"
49
50static void v9fs_clear_inode(struct inode *);
51static struct super_operations v9fs_super_ops;
52
53/**
54 * v9fs_clear_inode - release an inode
55 * @inode: inode to release
56 *
57 */
58
59static void v9fs_clear_inode(struct inode *inode)
60{
61 filemap_fdatawrite(inode->i_mapping);
62}
63
64/**
65 * v9fs_set_super - set the superblock
66 * @s: super block
67 * @data: file system specific data
68 *
69 */
70
71static int v9fs_set_super(struct super_block *s, void *data)
72{
73 s->s_fs_info = data;
74 return set_anon_super(s, data);
75}
76
77/**
78 * v9fs_fill_super - populate superblock with info
79 * @sb: superblock
80 * @v9ses: session information
81 *
82 */
83
84static void
85v9fs_fill_super(struct super_block *sb, struct v9fs_session_info *v9ses,
86 int flags)
87{
88 sb->s_maxbytes = MAX_LFS_FILESIZE;
89 sb->s_blocksize_bits = fls(v9ses->maxdata - 1);
90 sb->s_blocksize = 1 << sb->s_blocksize_bits;
91 sb->s_magic = V9FS_MAGIC;
92 sb->s_op = &v9fs_super_ops;
93
94 sb->s_flags = flags | MS_ACTIVE | MS_SYNCHRONOUS | MS_DIRSYNC |
95 MS_NODIRATIME | MS_NOATIME;
96}
97
98/**
99 * v9fs_get_sb - mount a superblock
100 * @fs_type: file system type
101 * @flags: mount flags
102 * @dev_name: device name that was mounted
103 * @data: mount options
104 *
105 */
106
107static struct super_block *v9fs_get_sb(struct file_system_type
108 *fs_type, int flags,
109 const char *dev_name, void *data)
110{
111 struct super_block *sb = NULL;
112 struct v9fs_fcall *fcall = NULL;
113 struct inode *inode = NULL;
114 struct dentry *root = NULL;
115 struct v9fs_session_info *v9ses = NULL;
116 struct v9fs_fid *root_fid = NULL;
117 int mode = S_IRWXUGO | S_ISVTX;
118 uid_t uid = current->fsuid;
119 gid_t gid = current->fsgid;
120 int stat_result = 0;
121 int newfid = 0;
122 int retval = 0;
123
124 dprintk(DEBUG_VFS, " \n");
125
126 v9ses = kcalloc(1, sizeof(struct v9fs_session_info), GFP_KERNEL);
127 if (!v9ses)
128 return ERR_PTR(-ENOMEM);
129
130 if ((newfid = v9fs_session_init(v9ses, dev_name, data)) < 0) {
131 dprintk(DEBUG_ERROR, "problem initiating session\n");
132 retval = newfid;
133 goto free_session;
134 }
135
136 sb = sget(fs_type, NULL, v9fs_set_super, v9ses);
137
138 v9fs_fill_super(sb, v9ses, flags);
139
140 inode = v9fs_get_inode(sb, S_IFDIR | mode);
141 if (IS_ERR(inode)) {
142 retval = PTR_ERR(inode);
143 goto put_back_sb;
144 }
145
146 inode->i_uid = uid;
147 inode->i_gid = gid;
148
149 root = d_alloc_root(inode);
150
151 if (!root) {
152 retval = -ENOMEM;
153 goto release_inode;
154 }
155
156 sb->s_root = root;
157
158 /* Setup the Root Inode */
159 root_fid = v9fs_fid_create(root);
160 if (root_fid == NULL) {
161 retval = -ENOMEM;
162 goto release_dentry;
163 }
164
165 root_fid->fidopen = 0;
166 root_fid->v9ses = v9ses;
167
168 stat_result = v9fs_t_stat(v9ses, newfid, &fcall);
169 if (stat_result < 0) {
170 dprintk(DEBUG_ERROR, "stat error\n");
171 v9fs_t_clunk(v9ses, newfid, NULL);
172 v9fs_put_idpool(newfid, &v9ses->fidpool);
173 } else {
174 root_fid->fid = newfid;
175 root_fid->qid = fcall->params.rstat.stat->qid;
176 root->d_inode->i_ino =
177 v9fs_qid2ino(&fcall->params.rstat.stat->qid);
178 v9fs_mistat2inode(fcall->params.rstat.stat, root->d_inode, sb);
179 }
180
181 kfree(fcall);
182
183 if (stat_result < 0) {
184 retval = stat_result;
185 goto release_dentry;
186 }
187
188 return sb;
189
190 release_dentry:
191 dput(sb->s_root);
192
193 release_inode:
194 iput(inode);
195
196 put_back_sb:
197 up_write(&sb->s_umount);
198 deactivate_super(sb);
199 v9fs_session_close(v9ses);
200
201 free_session:
202 kfree(v9ses);
203
204 return ERR_PTR(retval);
205}
206
207/**
208 * v9fs_kill_super - Kill Superblock
209 * @s: superblock
210 *
211 */
212
213static void v9fs_kill_super(struct super_block *s)
214{
215 struct v9fs_session_info *v9ses = s->s_fs_info;
216
217 dprintk(DEBUG_VFS, " %p\n", s);
218
219 v9fs_dentry_release(s->s_root); /* clunk root */
220
221 kill_anon_super(s);
222
223 v9fs_session_close(v9ses);
224 kfree(v9ses);
225 dprintk(DEBUG_VFS, "exiting kill_super\n");
226}
227
228/**
229 * v9fs_show_options - Show mount options in /proc/mounts
230 * @m: seq_file to write to
231 * @mnt: mount descriptor
232 *
233 */
234
235static int v9fs_show_options(struct seq_file *m, struct vfsmount *mnt)
236{
237 struct v9fs_session_info *v9ses = mnt->mnt_sb->s_fs_info;
238
239 if (v9ses->debug != 0)
240 seq_printf(m, ",debug=%u", v9ses->debug);
241 if (v9ses->port != V9FS_PORT)
242 seq_printf(m, ",port=%u", v9ses->port);
243 if (v9ses->maxdata != 9000)
244 seq_printf(m, ",msize=%u", v9ses->maxdata);
245 if (v9ses->afid != ~0)
246 seq_printf(m, ",afid=%u", v9ses->afid);
247 if (v9ses->proto == PROTO_UNIX)
248 seq_puts(m, ",proto=unix");
249 if (v9ses->extended == 0)
250 seq_puts(m, ",noextend");
251 if (v9ses->nodev == 1)
252 seq_puts(m, ",nodevmap");
253 seq_printf(m, ",name=%s", v9ses->name);
254 seq_printf(m, ",aname=%s", v9ses->remotename);
255 seq_printf(m, ",uid=%u", v9ses->uid);
256 seq_printf(m, ",gid=%u", v9ses->gid);
257 return 0;
258}
259
260static void
261v9fs_umount_begin(struct super_block *sb)
262{
263 struct v9fs_session_info *v9ses = sb->s_fs_info;
264
265 v9fs_session_cancel(v9ses);
266}
267
268static struct super_operations v9fs_super_ops = {
269 .statfs = simple_statfs,
270 .clear_inode = v9fs_clear_inode,
271 .show_options = v9fs_show_options,
272 .umount_begin = v9fs_umount_begin,
273};
274
275struct file_system_type v9fs_fs_type = {
276 .name = "9P",
277 .get_sb = v9fs_get_sb,
278 .kill_sb = v9fs_kill_super,
279 .owner = THIS_MODULE,
280};