ninenano

Client implementation of the 9P protocol for constrained devices

git clone https://git.8pit.net/ninenano.git

  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/**@}*/