1/*
2 * Copyright � 2007 Alistair Crooks.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote
13 *    products derived from this software without specific prior written
14 *    permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
17 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
22 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28#include <sys/types.h>
29#include <sys/stat.h>
30
31#include <ctype.h>
32#include <dirent.h>
33#include <err.h>
34#include <errno.h>
35#include <fcntl.h>
36#include <fuse.h>
37#include <signal.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <string.h>
41#include <time.h>
42#include <unistd.h>
43
44#include "virtdir.h"
45#include "defs.h"
46
47#ifndef PREFIX
48#define PREFIX		""
49#endif
50
51#ifndef DEF_CONF_FILE
52#define DEF_CONF_FILE	"/etc/icfs.conf"
53#endif
54
55DEFINE_ARRAY(strv_t, char *);
56
57static struct stat	 vfs;		/* stat info of directory */
58static virtdir_t	 tree;		/* virtual directory tree */
59static int		 verbose; 	/* how chatty are we? */
60
61
62
63
64
65/********************************************************************/
66
67/* convert a string to lower case */
68static char *
69strtolower(const char *path, char *name, size_t size)
70{
71	const char	*cp;
72	char		*np;
73
74	for (cp = path, np = name ; (int)(np - name) < size ; np++, cp++) {
75		*np = tolower((unsigned)*cp);
76	}
77	return name;
78}
79
80/* add a name and its lower case entry */
81static void
82add_entry(virtdir_t *tp, const char *name, uint8_t type)
83{
84	char	 icname[MAXPATHLEN];
85	char	*root;
86	int	 len;
87
88	root = virtdir_rootdir(&tree);
89	len = strlen(root);
90	strtolower(&name[len], icname, sizeof(icname));
91	virtdir_add(tp, &name[len], strlen(name) - len, type, icname,
92				strlen(icname));
93}
94
95/* file system operations start here */
96
97/* perform the stat operation */
98static int
99icfs_getattr(const char *path, struct stat *st)
100{
101	virt_dirent_t	*ep;
102	char		 name[MAXPATHLEN];
103
104	(void) memset(st, 0x0, sizeof(*st));
105	if (strcmp(path, "/") == 0) {
106		st->st_mode = S_IFDIR | 0755;
107		st->st_nlink = 2;
108		return 0;
109	}
110	if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
111		return -ENOENT;
112	}
113	(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
114	if (stat(name, st) < 0) {
115		return -errno;
116	}
117	return 0;
118}
119
120/* readdir operation */
121static int
122icfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
123	      off_t offset, struct fuse_file_info *fi)
124{
125	virt_dirent_t	*ep;
126	VIRTDIR		*dirp;
127	char		 name[MAXPATHLEN];
128
129	(void) fi;
130
131	if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
132		return -ENOENT;
133	}
134	(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
135	if ((dirp = openvirtdir(&tree, ep->name)) == NULL) {
136		return -ENOENT;
137	}
138	filler(buf, ".", NULL, 0);
139	filler(buf, "..", NULL, 0);
140	while ((ep = readvirtdir(dirp)) != NULL) {
141		strtolower(ep->d_name, name, sizeof(name));
142		(void) filler(buf, name, NULL, 0);
143	}
144	(void) closevirtdir(dirp);
145	return 0;
146}
147
148/* open the file in the file system */
149static int
150icfs_open(const char *path, struct fuse_file_info *fi)
151{
152	return 0;
153}
154
155/* read the file's contents in the file system */
156static int
157icfs_read(const char *path, char *buf, size_t size, off_t offset,
158	   struct fuse_file_info * fi)
159{
160	virt_dirent_t	*ep;
161	char		 name[MAXPATHLEN];
162	int		 fd;
163	int		 cc;
164
165	(void) fi;
166
167	if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
168		return -ENOENT;
169	}
170	(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
171	if ((fd = open(name, O_RDONLY, 0666)) < 0) {
172		return -ENOENT;
173	}
174	if (lseek(fd, offset, SEEK_SET) < 0) {
175		(void) close(fd);
176		return -EBADF;
177	}
178	if ((cc = read(fd, buf, size)) < 0) {
179		(void) close(fd);
180		return -errno;
181	}
182	(void) close(fd);
183	return cc;
184}
185
186/* write the file's contents in the file system */
187static int
188icfs_write(const char *path, const char *buf, size_t size, off_t offset,
189	   struct fuse_file_info * fi)
190{
191	virt_dirent_t	*ep;
192	char		 name[MAXPATHLEN];
193	int		 fd;
194	int		 cc;
195
196	(void) fi;
197
198	if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
199		return -ENOENT;
200	}
201	(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
202	if ((fd = open(name, O_WRONLY, 0666)) < 0) {
203		return -ENOENT;
204	}
205	if (lseek(fd, offset, SEEK_SET) < 0) {
206		(void) close(fd);
207		return -EBADF;
208	}
209	if ((cc = write(fd, buf, size)) < 0) {
210		(void) close(fd);
211		return -errno;
212	}
213	(void) close(fd);
214	return cc;
215}
216
217/* fill in the statvfs struct */
218static int
219icfs_statfs(const char *path, struct statvfs *st)
220{
221	(void) memset(st, 0x0, sizeof(*st));
222	st->f_bsize = st->f_frsize = st->f_iosize = 512;
223	st->f_owner = vfs.st_uid;
224	st->f_files = 1;
225	return 0;
226}
227
228/* "remove" a file */
229static int
230icfs_unlink(const char *path)
231{
232	virt_dirent_t	*ep;
233	char		 name[MAXPATHLEN];
234
235	if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
236		return -ENOENT;
237	}
238	(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
239	if (unlink(name) < 0) {
240		return -errno;
241	}
242	/* XXX - delete entry */
243	return 0;
244}
245
246/* check the access on a file */
247static int
248icfs_access(const char *path, int acc)
249{
250	virt_dirent_t	*ep;
251	char		 name[MAXPATHLEN];
252
253	if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
254		return -ENOENT;
255	}
256	(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
257	if (access(name, acc) < 0) {
258		return -errno;
259	}
260	return 0;
261}
262
263/* change the mode of a file */
264static int
265icfs_chmod(const char *path, mode_t mode)
266{
267	virt_dirent_t	*ep;
268	char		 name[MAXPATHLEN];
269
270	if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
271		return -ENOENT;
272	}
273	(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
274	if (chmod(name, mode) < 0) {
275		return -errno;
276	}
277	return 0;
278}
279
280/* change the owner and group of a file */
281static int
282icfs_chown(const char *path, uid_t uid, gid_t gid)
283{
284	virt_dirent_t	*ep;
285	char		 name[MAXPATHLEN];
286
287	if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
288		return -ENOENT;
289	}
290	(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
291	if (lchown(name, uid, gid) < 0) {
292		return -errno;
293	}
294	return 0;
295}
296
297/* "rename" a file */
298static int
299icfs_rename(const char *from, const char *to)
300{
301#if 0
302	char	fromname[MAXPATHLEN];
303	char	toname[MAXPATHLEN];
304
305	virt_dirent_t	*ep;
306
307	if ((ep = virtdir_find_tgt(&tree, from, strlen(from))) == NULL) {
308		return -ENOENT;
309	}
310	(void) snprintf(toname, sizeof(toname), "%s%s", dirs.v[0], to);
311	if (!mkdirs(toname)) {
312		return -ENOENT;
313	}
314	if (rename(fromname, toname) < 0) {
315		return -EPERM;
316	}
317#endif
318	return 0;
319}
320
321/* create a file */
322static int
323icfs_create(const char *path, mode_t mode, struct fuse_file_info *fi)
324{
325	virt_dirent_t	*ep;
326	char		*slash;
327	char		 name[MAXPATHLEN];
328	int		 fd;
329
330	if ((slash = strrchr(path, '/')) == NULL) {
331		return -ENOENT;
332	}
333	if ((ep = virtdir_find_tgt(&tree, path, (int)(slash - path) - 1)) == NULL) {
334		return -ENOENT;
335	}
336	(void) snprintf(name, sizeof(name), "%s/%s%s", virtdir_rootdir(&tree), ep->name, slash);
337	if ((fd = open(name, O_RDWR | O_CREAT | O_EXCL, 0666)) < 0) {
338		return -EPERM;
339	}
340	(void) close(fd);
341	add_entry(&tree, name, 'f');
342	return 0;
343}
344
345/* create a special node */
346static int
347icfs_mknod(const char *path, mode_t mode, dev_t d)
348{
349	virt_dirent_t	*ep;
350	char		*slash;
351	char		 name[MAXPATHLEN];
352
353	if ((slash = strrchr(path, '/')) == NULL) {
354		return -ENOENT;
355	}
356	if ((ep = virtdir_find_tgt(&tree, path, (int)(slash - path) - 1)) == NULL) {
357		return -ENOENT;
358	}
359	(void) snprintf(name, sizeof(name), "%s/%s/%s", virtdir_rootdir(&tree), ep->name, slash + 1);
360	if (mknod(name, mode, d) < 0) {
361		return -EPERM;
362	}
363	add_entry(&tree, name, ((mode & S_IFMT) == S_IFCHR) ? 'c' : 'b');
364	return 0;
365}
366
367/* create a directory */
368static int
369icfs_mkdir(const char *path, mode_t mode)
370{
371	virt_dirent_t	*ep;
372	char		*slash;
373	char		 name[MAXPATHLEN];
374
375	if ((slash = strrchr(path, '/')) == NULL) {
376		return -ENOENT;
377	}
378	if ((ep = virtdir_find_tgt(&tree, path, (int)(slash - path) - 1)) == NULL) {
379		return -EEXIST;
380	}
381	(void) snprintf(name, sizeof(name), "%s/%s/%s", virtdir_rootdir(&tree), ep->name, slash + 1);
382	if (mkdir(name, mode) < 0) {
383		return -EPERM;
384	}
385	add_entry(&tree, name, 'd');
386	return 0;
387}
388
389/* create a symbolic link */
390static int
391icfs_symlink(const char *path, const char *tgt)
392{
393	virt_dirent_t	*ep;
394	char		*slash;
395	char		 name[MAXPATHLEN];
396
397	if ((slash = strrchr(path, '/')) == NULL) {
398		return -ENOENT;
399	}
400	if ((ep = virtdir_find_tgt(&tree, path, (int)(slash - path) - 1)) == NULL) {
401		return -EEXIST;
402	}
403	(void) snprintf(name, sizeof(name), "%s/%s/%s", virtdir_rootdir(&tree), ep->name, slash + 1);
404	if (symlink(name, tgt) < 0) {
405		return -EPERM;
406	}
407	add_entry(&tree, name, 'l');
408	return 0;
409}
410
411/* create a link */
412static int
413icfs_link(const char *path, const char *tgt)
414{
415	virt_dirent_t	*ep;
416	char		*slash;
417	char		 name[MAXPATHLEN];
418
419	if ((slash = strrchr(path, '/')) == NULL) {
420		return -ENOENT;
421	}
422	if ((ep = virtdir_find_tgt(&tree, path, (int)(slash - path) - 1)) == NULL) {
423		return -EEXIST;
424	}
425	(void) snprintf(name, sizeof(name), "%s/%s/%s", virtdir_rootdir(&tree), ep->name, slash + 1);
426	if (link(name, tgt) < 0) {
427		return -errno;
428	}
429	add_entry(&tree, name, 'f');	/* XXX */
430	return 0;
431}
432
433/* read the contents of a symbolic link */
434static int
435icfs_readlink(const char *path, char *buf, size_t size)
436{
437	virt_dirent_t	*ep;
438	char		 name[MAXPATHLEN];
439
440	if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
441		return -ENOENT;
442	}
443	(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
444	if (readlink(name, buf, size) < 0) {
445		return -errno;
446	}
447	return 0;
448}
449
450/* remove a directory */
451static int
452icfs_rmdir(const char *path)
453{
454	virt_dirent_t	*ep;
455	char		 name[MAXPATHLEN];
456
457	if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
458		return -ENOENT;
459	}
460	(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
461	if (rmdir(name) < 0) {
462		return -errno;
463	}
464	/* XXX - delete entry */
465	return 0;
466}
467
468/* truncate a file */
469static int
470icfs_truncate(const char *path, off_t size)
471{
472	virt_dirent_t	*ep;
473	char		 name[MAXPATHLEN];
474
475	if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
476		return -ENOENT;
477	}
478	(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
479	if (truncate(name, size) < 0) {
480		return -errno;
481	}
482	return 0;
483}
484
485/* set utimes on a file */
486static int
487icfs_utime(const char *path, struct utimbuf *t)
488{
489	virt_dirent_t	*ep;
490	char		 name[MAXPATHLEN];
491
492	if ((ep = virtdir_find_tgt(&tree, path, strlen(path))) == NULL) {
493		return -ENOENT;
494	}
495	(void) snprintf(name, sizeof(name), "%s/%s", virtdir_rootdir(&tree), ep->name);
496	if (utime(name, t) < 0) {
497		return -errno;
498	}
499	return 0;
500}
501
502/* operations struct */
503static struct fuse_operations icfs_oper = {
504	.getattr = icfs_getattr,
505	.readlink = icfs_readlink,
506	.mknod = icfs_mknod,
507	.mkdir = icfs_mkdir,
508	.unlink = icfs_unlink,
509	.rmdir = icfs_rmdir,
510	.symlink = icfs_symlink,
511	.rename = icfs_rename,
512	.link = icfs_link,
513	.chmod = icfs_chmod,
514	.chown = icfs_chown,
515	.truncate = icfs_truncate,
516	.utime = icfs_utime,
517	.open = icfs_open,
518	.read = icfs_read,
519	.write = icfs_write,
520	.statfs = icfs_statfs,
521	.readdir = icfs_readdir,
522	.access = icfs_access,
523	.create = icfs_create
524};
525
526/* build up a virtdir from the information in the file system */
527static int
528dodir(virtdir_t *tp, char *rootdir, const char *subdir)
529{
530	struct dirent	*dp;
531	struct stat	 st;
532	struct stat	 dir;
533	struct stat	 f;
534	struct stat	 l;
535	char		 icname[MAXPATHLEN];
536	char		 name[MAXPATHLEN];
537	char		 type;
538	DIR		*dirp;
539	int		 len;
540
541	if (tp->v == NULL) {
542		(void) stat(".", &dir);
543		(void) memcpy(&f, &dir, sizeof(f));
544		f.st_mode = S_IFREG | 0644;
545		(void) memcpy(&l, &f, sizeof(l));
546		l.st_mode = S_IFLNK | 0755;
547		virtdir_init(tp, rootdir, &dir, &f, &l);
548		virtdir_add(tp, "/", 1, 'd', "/", 1);
549	}
550	(void) snprintf(name, sizeof(name), "%s/%s", rootdir, subdir);
551	if ((dirp = opendir(name)) == NULL) {
552		warn("dodir: can't opendir `%s'", name);
553		return 0;
554	}
555	len = strlen(tp->rootdir);
556	while ((dp = readdir(dirp)) != NULL) {
557		if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) {
558			continue;
559		}
560		(void) snprintf(name, sizeof(name), "%s%s%s%s%s", rootdir, (subdir[0] == 0x0) ? "" : "/", subdir,  "/", dp->d_name);
561		if (stat(name, &st) < 0) {
562			warnx("can't stat `%s'", name);
563			continue;
564		}
565		switch (st.st_mode & S_IFMT) {
566		case S_IFDIR:
567			type = 'd';
568			break;
569		case S_IFREG:
570			type = 'f';
571			break;
572		case S_IFLNK:
573			type = 'l';
574			break;
575		case S_IFBLK:
576			type = 'b';
577			break;
578		case S_IFCHR:
579			type = 'c';
580			break;
581		default:
582			type = '?';
583			break;
584		}
585		if (!virtdir_find(tp, &name[len], strlen(name) - len)) {
586			strtolower(&name[len], icname, sizeof(icname));
587			virtdir_add(tp, &name[len], strlen(name) - len, type, icname,
588				strlen(icname));
589		}
590		if (type == 'd') {
591			dodir(tp, rootdir, &name[len + 1]);
592		}
593	}
594	(void) closedir(dirp);
595	return 1;
596}
597
598int
599main(int argc, char **argv)
600{
601	int	 i;
602
603	while ((i = getopt(argc, argv, "f:v")) != -1) {
604		switch(i) {
605		case 'v':
606			verbose = 1;
607			break;
608		}
609	}
610#if 0
611	(void) daemon(1, 1);
612#endif
613	dodir(&tree, argv[optind], "");
614	return fuse_main(argc, argv, &icfs_oper, NULL);
615}
616