1#include <string.h>
2#include <unistd.h>
3#include <fcntl.h>
4#include <errno.h>
5
6#include "9p.h"
7#include "vfs.h"
8#include "9pfs.h"
9#include "9util.h"
10
11#define ENABLE_DEBUG (0)
12#include "debug.h"
13
14/**
15 * Split a given path into the directory and file portition.
16 *
17 * @param buf Pointer to a temporary buffer used to create a copy of the
18 * path argument in order to prevent modification of the latter.
19 * @param bufsiz Size of the temporary buffer.
20 * @param path Pointer to the path which should be split.
21 * @param dname Pointer to a memory location which should be set to the
22 * address of the directory portion.
23 * @return Pointer to the file portion of the name.
24 */
25static char*
26breakpath(char *buf, size_t bufsiz, const char *path, char **dname)
27{
28 char *bname;
29
30 strncpy(buf, path, bufsiz);
31 *dname = buf;
32
33 bname = strrchr(buf, '/');
34 *bname++ = '\0';
35 return bname;
36}
37
38/**
39 * @defgroup _9pfs_fs_ops File system operations.
40 *
41 * @{
42 */
43
44static int
45_9pfs_mount(vfs_mount_t *mountp)
46{
47 int r;
48 _9pfid *f;
49 _9pfs *fs;
50
51 fs = mountp->private_data;
52
53 mutex_init(&fs->mtx);
54 if ((r = _9pversion(&fs->ctx)))
55 return r;
56 if ((r = _9pattach(&fs->ctx, &f, fs->uname, fs->aname)))
57 return r;
58
59 (void)f;
60 return 0;
61}
62
63static int
64_9pfs_umount(vfs_mount_t *mountp)
65{
66 (void)mountp;
67
68 return 0;
69}
70
71static int
72_9pfs_unlink(vfs_mount_t *mountp, const char *name)
73{
74 _9pfid *f;
75 _9pfs *fs;
76 int r;
77
78 r = 0;
79 fs = mountp->private_data;
80
81 mutex_lock(&fs->mtx);
82 if (_9pwalk(&fs->ctx, &f, (char*)name)) {
83 r = -ENOENT;
84 goto ret;
85 }
86 if (_9premove(&fs->ctx, f)) {
87 r = -EACCES;
88 goto ret;
89 }
90
91ret:
92 mutex_unlock(&fs->mtx);
93 return r;
94}
95
96static int
97_9pfs_mkdir(vfs_mount_t *mountp, const char *name, mode_t mode)
98{
99 _9pfid *f;
100 char buf[VFS_NAME_MAX + 1];
101 char *dname, *bname;
102 _9pfs *fs;
103 int r;
104
105 r = 0;
106 fs = mountp->private_data;
107
108 mutex_lock(&fs->mtx);
109 if (!_9pwalk(&fs->ctx, &f, (char*)name)) {
110 _9pclunk(&fs->ctx, f);
111 r = -EEXIST;
112 goto ret;
113 }
114
115 bname = breakpath(buf, sizeof(buf), name, &dname);
116 DEBUG("Creating directory '%s' in directory '%s'\n", bname, dname);
117
118 if (_9pwalk(&fs->ctx, &f, dname)) {
119 r = -EACCES;
120 goto ret;
121 }
122
123 mode &= 0777;
124 mode |= DMDIR;
125
126 if (_9pcreate(&fs->ctx, f, bname, mode, OREAD)) {
127 _9pclunk(&fs->ctx, f);
128 r = -EACCES;
129 goto ret;
130 }
131
132 _9pclunk(&fs->ctx, f);
133
134ret:
135 mutex_unlock(&fs->mtx);
136 return r;
137}
138
139static int
140_9pfs_rmdir(vfs_mount_t *mountp, const char *name)
141{
142 return _9pfs_unlink(mountp, name);
143}
144
145static int
146_9pfs_stat(vfs_mount_t *mountp, const char *restrict name, struct stat *restrict buf)
147{
148 _9pfid *f;
149 _9pfs *fs;
150 int r;
151
152 fs = mountp->private_data;
153 r = 0;
154
155 mutex_lock(&fs->mtx);
156 if (_9pwalk(&fs->ctx, &f, (char*)name)) {
157 r = -ENOENT;
158 goto ret;
159 }
160
161 if (_9pstat(&fs->ctx, f, buf)) {
162 _9pclunk(&fs->ctx, f);
163 r = -EACCES;
164 goto ret;
165 }
166
167 _9pclunk(&fs->ctx, f);
168
169ret:
170 mutex_unlock(&fs->mtx);
171 return r;
172}
173
174/**@}*/
175
176/**
177 * @defgroup _9pfs_file_ops File operations.
178 *
179 * @{
180 */
181
182static int
183_9pfs_close(vfs_file_t *filp)
184{
185 _9pfid *f;
186 _9pfs *fs;
187
188 f = filp->private_data.ptr;
189 fs = filp->mp->private_data;
190
191 mutex_lock(&fs->mtx);
192 if (_9pclunk(&fs->ctx, f) == -EBADF) {
193 mutex_unlock(&fs->mtx);
194 return -EBADF;
195 }
196 mutex_unlock(&fs->mtx);
197
198 return 0;
199}
200
201static int
202_9pfs_fstat(vfs_file_t *filp, struct stat *buf)
203{
204 _9pfid *f;
205 _9pfs *fs;
206
207 f = filp->private_data.ptr;
208 fs = filp->mp->private_data;
209
210 mutex_lock(&fs->mtx);
211 if (_9pstat(&fs->ctx, f, buf)) {
212 mutex_unlock(&fs->mtx);
213 return -EACCES;
214 }
215 mutex_unlock(&fs->mtx);
216
217 return 0;
218}
219
220static off_t _9pfs_lseek(vfs_file_t *filp, off_t off, int whence)
221{
222 _9pfid *f;
223 _9pfs *fs;
224 struct stat st;
225
226 f = filp->private_data.ptr;
227 fs = filp->mp->private_data;
228
229 switch (whence) {
230 case SEEK_SET:
231 break;
232 case SEEK_CUR:
233 off += f->off;
234 break;
235 case SEEK_END:
236 mutex_lock(&fs->mtx);
237 if (_9pstat(&fs->ctx, f, &st)) {
238 mutex_unlock(&fs->mtx);
239 return -EINVAL;
240 }
241 mutex_unlock(&fs->mtx);
242
243 off += st.st_size;
244 break;
245 default:
246 return -EINVAL;
247 }
248
249 if (off < 0)
250 return -EINVAL;
251
252 mutex_lock(&fs->mtx);
253 f->off = off;
254 mutex_unlock(&fs->mtx);
255
256 return off;
257}
258
259static int
260_9pfs_open(vfs_file_t *filp, const char *name, int flags, mode_t mode, const char *abs_path)
261{
262 int r, fl;
263 _9pfid *f;
264 _9pfs *fs;
265 char buf[VFS_NAME_MAX + 1];
266 char *bname, *dname;
267
268 (void)abs_path;
269
270 r = 0;
271 fs = filp->mp->private_data;
272
273 /* Convert the mode. This assumes that OREAD == O_RDONLY,
274 * O_WRONLY == OWRITE and O_RDWR == ORDWR which should always be
275 * the case on RIOT. */
276 fl = flags & O_ACCMODE;
277 if (flags & O_TRUNC)
278 fl |= OTRUNC;
279
280 mutex_lock(&fs->mtx);
281 if (!_9pwalk(&fs->ctx, &f, (char*)name)) {
282 if (_9popen(&fs->ctx, f, fl)) {
283 _9pclunk(&fs->ctx, f);
284 r = -EACCES;
285 goto ret;
286 }
287
288 filp->private_data.ptr = f;
289 goto ret;
290 } else if (flags & O_CREAT) {
291 bname = breakpath(buf, sizeof(buf), name, &dname);
292 if (_9pwalk(&fs->ctx, &f, dname)) {
293 r = -ENOENT;
294 goto ret;
295 }
296
297 if (_9pcreate(&fs->ctx, f, bname, mode, fl)) {
298 _9pclunk(&fs->ctx, f);
299 r = -EACCES;
300 goto ret;
301 }
302
303 filp->private_data.ptr = f;
304 goto ret;
305 } else {
306 r = -ENOENT;
307 goto ret;
308 }
309
310ret:
311 mutex_unlock(&fs->mtx);
312 return r;
313}
314
315static ssize_t
316_9pfs_read(vfs_file_t *filp, void *dest, size_t nbytes)
317{
318 ssize_t ret;
319 _9pfid *f;
320 _9pfs *fs;
321
322 f = filp->private_data.ptr;
323 fs = filp->mp->private_data;
324
325 mutex_lock(&fs->mtx);
326 if ((ret = _9pread(&fs->ctx, f, dest, nbytes)) < 0) {
327 mutex_unlock(&fs->mtx);
328 return -EIO;
329 }
330 mutex_unlock(&fs->mtx);
331
332 return ret;
333}
334
335static ssize_t
336_9pfs_write(vfs_file_t *filp, const void *src, size_t nbytes)
337{
338 ssize_t ret;
339 _9pfid *f;
340 _9pfs *fs;
341
342 f = filp->private_data.ptr;
343 fs = filp->mp->private_data;
344
345 mutex_lock(&fs->mtx);
346 if ((ret = _9pwrite(&fs->ctx, f, (void*)src, nbytes)) < 0) {
347 mutex_unlock(&fs->mtx);
348 return -EIO;
349 }
350 mutex_unlock(&fs->mtx);
351
352 return ret;
353}
354
355/**@}*/
356
357/**
358 * @defgroup _9pfs_dir_ops Directory operations.
359 *
360 * @{
361 */
362
363static int
364_9pfs_opendir(vfs_DIR *dirp, const char *dirname, const char *abs_path)
365{
366 _9pfid *f;
367 _9pfs *fs;
368 int r;
369
370 (void)abs_path;
371
372 r = 0;
373 fs = dirp->mp->private_data;
374
375 mutex_lock(&fs->mtx);
376 if (_9pwalk(&fs->ctx, &f, (char*)dirname)) {
377 r = -ENOENT;
378 goto ret;
379 }
380
381 if (_9popen(&fs->ctx, f, OREAD)) {
382 _9pclunk(&fs->ctx, f);
383 r = -EACCES;
384 goto ret;
385 }
386
387 if (!(f->qid.type & QTDIR)) {
388 _9pclunk(&fs->ctx, f);
389 r = -ENOTDIR;
390 goto ret;
391 }
392
393 dirp->private_data.ptr = f;
394
395ret:
396 mutex_unlock(&fs->mtx);
397 return r;
398}
399
400static int
401_9pfs_readdir(vfs_DIR *dirp, vfs_dirent_t *entry)
402{
403 ssize_t n;
404 _9pfid *f;
405 _9ppkt pkt;
406 _9pfs *fs;
407 char dest[_9P_MINSTSIZ + VFS_NAME_MAX + 1];
408 int r;
409
410 r = 0;
411 f = dirp->private_data.ptr;
412 fs = dirp->mp->private_data;
413
414 mutex_lock(&fs->mtx);
415 if ((n = _9pread(&fs->ctx, f, dest, sizeof(dest))) < 0) {
416 r = -EIO;
417 goto ret;
418 } else if (n == 0) {
419 goto ret;
420 }
421
422 pkt.len = n;
423 pkt.buf = (unsigned char*)dest;
424
425 /* Skip all the information we don't need. */
426 advbuf(&pkt, 2 * BIT16SZ + BIT32SZ + _9P_QIDSIZ + 3 * BIT32SZ + BIT64SZ);
427 if (hstring(entry->d_name, sizeof(entry->d_name), &pkt)) {
428 r = -EIO;
429 goto ret;
430 }
431
432 entry->d_ino = 0; /* XXX */
433 r = 1;
434
435ret:
436 mutex_unlock(&fs->mtx);
437 return r;
438}
439
440static int
441_9pfs_closedir(vfs_DIR *dirp)
442{
443 _9pfid *f;
444 _9pfs *fs;
445
446 f = dirp->private_data.ptr;
447 fs = dirp->mp->private_data;
448
449 mutex_lock(&fs->mtx);
450 if (_9pclunk(&fs->ctx, f) == -EBADF) {
451 mutex_unlock(&fs->mtx);
452 return -EBADF;
453 }
454 mutex_unlock(&fs->mtx);
455
456 return 0;
457}
458
459/**@}*/
460
461/**
462 * @defgroup fs Struct definitions.
463 *
464 * @{
465 */
466
467static const vfs_file_system_ops_t _9pfs_fs_ops = {
468 .mount = _9pfs_mount,
469 .umount = _9pfs_umount,
470 .rename = NULL, /* TODO */
471 .unlink = _9pfs_unlink,
472 .mkdir = _9pfs_mkdir,
473 .rmdir = _9pfs_rmdir,
474 .stat = _9pfs_stat,
475 .statvfs = NULL,
476 .fstatvfs = NULL, /* TODO */
477};
478
479static const vfs_file_ops_t _9pfs_file_ops = {
480 .close = _9pfs_close,
481 .fcntl = NULL,
482 .fstat = _9pfs_fstat,
483 .lseek = _9pfs_lseek,
484 .open = _9pfs_open,
485 .read = _9pfs_read,
486 .write = _9pfs_write,
487};
488
489static const vfs_dir_ops_t _9pfs_dir_ops = {
490 .opendir = _9pfs_opendir,
491 .readdir = _9pfs_readdir,
492 .closedir = _9pfs_closedir,
493};
494
495const vfs_file_system_t _9p_file_system = {
496 .f_op = &_9pfs_file_ops,
497 .fs_op = &_9pfs_fs_ops,
498 .d_op = &_9pfs_dir_ops,
499};
500
501/**@}*/