Initial revision
[linux-2.6.git] / fs / hostfs / humfs.c
1 /* 
2  * Copyright (C) 2004 Jeff Dike (jdike@addtoit.com)
3  * Licensed under the GPL
4  */
5
6 #include <linux/kernel.h>
7 #include <linux/list.h>
8 #include <linux/sched.h>
9 #include <linux/slab.h>
10 #include <linux/stat.h>
11 #include <linux/types.h>
12 #include <linux/errno.h>
13 #include <linux/string.h>
14 #include <linux/kdev_t.h>
15 #include "linux/init.h"
16 #include "linux/workqueue.h"
17 #include <asm/irq.h>
18 #include "hostfs.h"
19 #include "mem.h"
20 #include "os.h"
21 #include "mode.h"
22 #include "aio.h"
23 #include "irq_user.h"
24 #include "irq_kern.h"
25 #include "filehandle.h"
26 #include "metadata.h"
27
28 #define HUMFS_VERSION 2
29
30 static int humfs_stat_file(const char *path, struct externfs_data *ed, 
31                            dev_t *dev_out, unsigned long long *inode_out, 
32                            int *mode_out, int *nlink_out, int *uid_out, 
33                            int *gid_out, unsigned long long *size_out, 
34                            unsigned long *atime_out, unsigned long *mtime_out, 
35                            unsigned long *ctime_out, int *blksize_out, 
36                            unsigned long long *blocks_out)
37 {
38         struct humfs *mount = container_of(ed, struct humfs, ext);
39         const char *data_path[3] = { mount->data, path, NULL };
40         int err, mode, perms, major, minor;
41         char type;
42
43         err = host_stat_file(data_path, NULL, inode_out, mode_out, 
44                              nlink_out, NULL, NULL, size_out, atime_out, 
45                              mtime_out, ctime_out, blksize_out, blocks_out);
46         if(err)
47                 return(err);
48
49         err = (*mount->meta->ownerships)(path, &perms, uid_out, gid_out, 
50                                          &type, &major, &minor, mount);
51         if(err)
52                 return(err);
53
54         *mode_out = (*mode_out & ~S_IRWXUGO) | perms;
55
56         mode = 0;
57         switch(type){
58         case 'c':
59                 mode = S_IFCHR;
60                 *dev_out = MKDEV(major, minor);
61                 break;
62         case 'b':
63                 mode = S_IFBLK;
64                 *dev_out = MKDEV(major, minor);
65                 break;
66         case 's':
67                 mode = S_IFSOCK;
68                 break;
69         default:
70                 break;
71         }
72
73         if(mode != 0)
74                 *mode_out = (*mode_out & ~S_IFMT) | mode;
75
76         return(0);
77 }
78
79 static int meta_type(const char *path, int *dev_out, void *m)
80 {
81         struct humfs *mount = m;
82         int err, type, maj, min;
83         char c;
84
85         err = (*mount->meta->ownerships)(path, NULL, NULL, NULL, &c, &maj, 
86                                          &min, mount);
87         if(err)
88                 return(err);
89
90         if(c == 0)
91                 return(0);
92
93         if(dev_out)
94                 *dev_out = MKDEV(maj, min);
95
96         switch(c){
97         case 'c':
98                 type = OS_TYPE_CHARDEV;
99                 break;
100         case 'b':
101                 type = OS_TYPE_BLOCKDEV;
102                 break;
103         case 'p':
104                 type = OS_TYPE_FIFO;
105                 break;
106         case 's':
107                 type = OS_TYPE_SOCK;
108                 break;
109         default:
110                 type = -EINVAL;
111                 break;
112         }
113
114         return(type);
115 }
116
117 static int humfs_file_type(const char *path, int *dev_out, 
118                            struct externfs_data *ed)
119 {
120         struct humfs *mount = container_of(ed, struct humfs, ext);
121         const char *data_path[3] = { mount->data, path, NULL };
122         int type;
123
124         type = meta_type(path, dev_out, mount);
125         if(type != 0)
126                 return(type);
127
128         return(host_file_type(data_path, dev_out));
129 }
130
131 static char *humfs_data_name(struct inode *inode)
132 {
133         struct externfs_data *ed = inode_externfs_info(inode);
134         struct humfs *mount = container_of(ed, struct humfs, ext);
135
136         return(inode_name_prefix(inode, mount->data));
137 }
138
139 static struct externfs_inode *humfs_init_file(struct externfs_data *ed)
140 {
141         struct humfs *mount = container_of(ed, struct humfs, ext);
142         struct humfs_file *hf;
143
144         hf = (*mount->meta->init_file)();
145         if(hf == NULL)
146                 return(NULL);
147
148         hf->data.fd = -1;
149         return(&hf->ext);
150 }
151
152 static int humfs_open_file(struct externfs_inode *ext, char *path, int uid, 
153                            int gid, struct inode *inode, 
154                            struct externfs_data *ed)
155 {
156         struct humfs *mount = container_of(ed, struct humfs, ext);
157         struct humfs_file *hf = container_of(ext, struct humfs_file, ext);
158         const char *data_path[3] = { mount->data, path, NULL };
159         struct openflags flags;
160         char tmp[HOSTFS_BUFSIZE], *file;
161         int err = -ENOMEM;
162
163         file = get_path(data_path, tmp, sizeof(tmp));
164         if(file == NULL)
165                 goto out;
166
167         flags = of_rdwr(OPENFLAGS());
168         if(mount->direct)
169                 flags = of_direct(flags);
170
171         if(path == NULL)
172                 path = "";
173         err = (*mount->meta->open_file)(hf, path, inode, mount);
174         if(err)
175                 goto out_free;
176
177         err = open_filehandle(file, flags, 0, &hf->data);
178         if(err == -EISDIR)
179                 goto out;
180         else if(err == -EPERM){
181                 flags = of_set_rw(flags, 1, 0);
182                 err = open_filehandle(file, flags, 0, &hf->data);
183         }
184         
185         if(err)
186                 goto out_close;
187
188         hf->mount = mount;
189         is_reclaimable(&hf->data, humfs_data_name, inode);
190
191  out_free:
192         free_path(file, tmp);
193  out:
194         return(err);
195         
196  out_close:
197         (*mount->meta->close_file)(hf);
198         goto out_free;
199 }
200
201 static void *humfs_open_dir(char *path, int uid, int gid, 
202                             struct externfs_data *ed)
203 {
204         struct humfs *mount = container_of(ed, struct humfs, ext);
205         const char *data_path[3] = { mount->data, path, NULL };
206
207         return(host_open_dir(data_path));
208 }
209
210 static void humfs_close_dir(void *stream, struct externfs_data *ed)
211 {
212         os_close_dir(stream);
213 }
214
215 static char *humfs_read_dir(void *stream, unsigned long long *pos, 
216                             unsigned long long *ino_out, int *len_out, 
217                             struct externfs_data *ed)
218 {
219         struct humfs *mount = container_of(ed, struct humfs, ext);
220
221         return(generic_host_read_dir(stream, pos, ino_out, len_out, mount));
222 }
223
224 LIST_HEAD(humfs_replies);
225
226 struct humfs_aio {
227         struct aio_context aio;
228         struct list_head list;
229         void (*completion)(char *, int, void *);
230         char *buf;
231         int real_len;
232         int err;
233         void *data;
234 };
235
236 static int humfs_reply_fd = -1;
237
238 struct humfs_aio last_task_aio, last_intr_aio;
239 struct humfs_aio *last_task_aio_ptr, *last_intr_aio_ptr;
240
241 void humfs_work_proc(void *unused)
242 {
243         struct humfs_aio *aio;
244         unsigned long flags;
245
246         while(!list_empty(&humfs_replies)){
247                 local_irq_save(flags);
248                 aio = list_entry(humfs_replies.next, struct humfs_aio, list);
249
250                 last_task_aio = *aio;
251                 last_task_aio_ptr = aio;
252
253                 list_del(&aio->list);
254                 local_irq_restore(flags);
255
256                 if(aio->err >= 0)
257                         aio->err = aio->real_len;
258                 (*aio->completion)(aio->buf, aio->err, aio->data);
259                 kfree(aio);
260         }
261 }
262
263 DECLARE_WORK(humfs_work, humfs_work_proc, NULL);
264
265 static irqreturn_t humfs_interrupt(int irq, void *dev_id, 
266                                    struct pt_regs *unused)
267 {
268         struct aio_thread_reply reply;
269         struct humfs_aio *aio;
270         int err, fd = (int) dev_id;
271
272         while(1){
273                 err = os_read_file(fd, &reply, sizeof(reply));
274                 if(err < 0){
275                         if(err == -EAGAIN)
276                                 break;
277                         printk("humfs_interrupt - read returned err %d\n", 
278                                -err);
279                         return(IRQ_HANDLED);
280                 }
281                 aio = reply.data;
282                 aio->err = reply.err;
283                 list_add(&aio->list, &humfs_replies);
284                 last_intr_aio = *aio;
285                 last_intr_aio_ptr = aio;
286         }
287
288         if(!list_empty(&humfs_replies))
289                 schedule_work(&humfs_work);
290         reactivate_fd(fd, HUMFS_IRQ);
291         return(IRQ_HANDLED);
292 }
293
294 static int init_humfs_aio(void)
295 {
296         int fds[2], err;
297
298         err = os_pipe(fds, 1, 1);
299         if(err){
300                 printk("init_humfs_aio - pipe failed, err = %d\n", -err);
301                 goto out;
302         }
303
304         err = um_request_irq(HUMFS_IRQ, fds[0], IRQ_READ, humfs_interrupt,
305                              SA_INTERRUPT | SA_SAMPLE_RANDOM, "humfs", 
306                              (void *) fds[0]);
307         if(err){
308                 printk("init_humfs_aio - : um_request_irq failed, err = %d\n",
309                        err);
310                 goto out_close;
311         }
312
313         humfs_reply_fd = fds[1];
314         goto out;
315         
316  out_close:
317         os_close_file(fds[0]);
318         os_close_file(fds[1]);
319  out:
320         return(0);
321 }
322
323 __initcall(init_humfs_aio);
324
325 static int humfs_aio(enum aio_type type, int fd, unsigned long long offset,
326                      char *buf, int len, int real_len,
327                      void (*completion)(char *, int, void *), void *arg)
328 {
329         struct humfs_aio *aio;
330         int err = -ENOMEM;
331
332         aio = kmalloc(sizeof(*aio), GFP_KERNEL);
333         if(aio == NULL)
334                 goto out;
335         *aio = ((struct humfs_aio) { .aio       = INIT_AIO_CONTEXT,
336                                      .list      = LIST_HEAD_INIT(aio->list),
337                                      .completion= completion,
338                                      .buf       = buf,
339                                      .err       = 0,
340                                      .real_len  = real_len,
341                                      .data      = arg });
342
343         err = submit_aio(type, fd, buf, len, offset, humfs_reply_fd, aio);
344         if(err)
345                 (*completion)(buf, err, arg);
346
347  out:
348         return(err);
349 }
350
351 static int humfs_read_file(struct externfs_inode *ext,
352                            unsigned long long offset, char *buf, int len,
353                            int ignore_start, int ignore_end,
354                            void (*completion)(char *, int, void *), void *arg, 
355                            struct externfs_data *ed)
356 {
357         struct humfs_file *hf = container_of(ext, struct humfs_file, ext);
358         int fd = filehandle_fd(&hf->data);
359
360         if(fd < 0){
361                 (*completion)(buf, fd, arg);
362                 return(fd);
363         }
364
365         return(humfs_aio(AIO_READ, fd, offset, buf, len, len, completion, 
366                          arg));
367 }
368
369 static int humfs_write_file(struct externfs_inode *ext,
370                             unsigned long long offset, 
371                             const char *buf, int start, int len, 
372                             void (*completion)(char *, int, void *), void *arg,
373                             struct externfs_data *ed)
374 {
375         struct humfs *mount = container_of(ed, struct humfs, ext);
376         struct humfs_file *hf = container_of(ext, struct humfs_file, ext);
377         int err, orig_len = len, fd = filehandle_fd(&hf->data);
378
379         if(fd < 0){
380                 (*completion)((char *) buf, fd, arg);
381                 return(fd);
382         }
383
384         if(mount->direct)
385                 len = PAGE_SIZE;
386         else {
387                 offset += start;
388                 buf += start;
389         }
390
391         err = humfs_aio(AIO_WRITE, fd, offset, (char *) buf, len, orig_len, 
392                         completion, arg);
393
394         if(err < 0)
395                 return(err);
396
397         if(mount->direct)
398                 err = orig_len;
399
400         return(err);
401 }
402
403 static int humfs_map_file_page(struct externfs_inode *ext, 
404                                unsigned long long offset, char *buf, int w, 
405                                struct externfs_data *ed)
406 {
407         struct humfs_file *hf = container_of(ext, struct humfs_file, ext);
408         unsigned long long size, need;
409         int err, fd = filehandle_fd(&hf->data);
410
411         if(fd < 0)
412                 return(fd);
413
414         err = os_fd_size(fd, &size);
415         if(err)
416                 return(err);
417
418         need = offset + PAGE_SIZE;
419         if(size < need){
420                 err = os_truncate_fd(fd, need);
421                 if(err)
422                         return(err);
423         }
424         
425         return(physmem_subst_mapping(buf, fd, offset, w));
426 }
427
428 static void humfs_close_file(struct externfs_inode *ext,
429                              unsigned long long size)
430 {
431         struct humfs_file *hf = container_of(ext, struct humfs_file, ext);
432         int fd;
433
434         if(hf->data.fd == -1)
435                 return;
436
437         fd = filehandle_fd(&hf->data);
438         physmem_forget_descriptor(fd);
439         truncate_file(&hf->data, size);
440         close_file(&hf->data);
441
442         (*hf->mount->meta->close_file)(hf);
443 }
444
445 /* XXX Assumes that you can't make a normal file */
446
447 static int humfs_make_node(const char *path, int mode, int uid, int gid, 
448                            int type, int major, int minor, 
449                            struct externfs_data *ed)
450 {
451         struct humfs *mount = container_of(ed, struct humfs, ext);
452         struct file_handle fh;
453         const char *data_path[3] = { mount->data, path, NULL };
454         int err;
455         char t;
456
457         err = host_create_file(data_path, S_IRWXUGO, &fh);
458         if(err)
459                 goto out;
460
461         close_file(&fh);
462
463         switch(type){
464         case S_IFCHR:
465                 t = 'c';
466                 break;
467         case S_IFBLK:
468                 t = 'b';
469                 break;
470         case S_IFIFO:
471                 t = 'p';
472                 break;
473         case S_IFSOCK:
474                 t = 's';
475                 break;
476         default:
477                 err = -EINVAL;
478                 printk("make_node - bad node type : %d\n", type);
479                 goto out_rm;
480         }
481
482         err = (*mount->meta->make_node)(path, mode, uid, gid, t, major, minor, 
483                                         mount);
484         if(err)
485                 goto out_rm;
486
487  out:
488         return(err);
489
490  out_rm:
491         host_unlink_file(data_path);
492         goto out;
493 }
494                 
495 static int humfs_create_file(struct externfs_inode *ext, char *path, int mode,
496                              int uid, int gid, struct inode *inode, 
497                              struct externfs_data *ed)
498 {
499         struct humfs *mount = container_of(ed, struct humfs, ext);
500         struct humfs_file *hf = container_of(ext, struct humfs_file, ext);
501         const char *data_path[3] = { mount->data, path, NULL };
502         int err;
503
504         err = (*mount->meta->create_file)(hf, path, mode, uid, gid, inode, 
505                                           mount);
506         if(err)
507                 goto out;
508
509         err = host_create_file(data_path, S_IRWXUGO, &hf->data);
510         if(err)
511                 goto out_rm;
512
513         
514         is_reclaimable(&hf->data, humfs_data_name, inode);
515
516         return(0);
517
518  out_rm:
519         (*mount->meta->remove_file)(path, mount);
520         (*mount->meta->close_file)(hf);
521  out:
522         return(err);
523 }
524
525 static int humfs_set_attr(const char *path, struct externfs_iattr *attrs, 
526                           struct externfs_data *ed)
527 {
528         struct humfs *mount = container_of(ed, struct humfs, ext);
529         const char *data_path[3] = { mount->data, path, NULL };
530         int (*chown)(const char *, int, int, int, struct humfs *);
531         int err;
532
533         chown = mount->meta->change_ownerships;
534         if(attrs->ia_valid & EXTERNFS_ATTR_MODE){
535                 err = (*chown)(path, attrs->ia_mode, -1, -1, mount);
536                 if(err)
537                         return(err);
538         }
539         if(attrs->ia_valid & EXTERNFS_ATTR_UID){
540                 err = (*chown)(path, -1, attrs->ia_uid, -1, mount);
541                 if(err)
542                         return(err);
543         }
544         if(attrs->ia_valid & EXTERNFS_ATTR_GID){
545                 err = (*chown)(path, -1, -1, attrs->ia_gid, mount);
546                 if(err)
547                         return(err);
548         }
549
550         attrs->ia_valid &= ~(EXTERNFS_ATTR_MODE | EXTERNFS_ATTR_UID | 
551                              EXTERNFS_ATTR_GID);
552
553         return(host_set_attr(data_path, attrs));
554 }
555
556 static int humfs_make_symlink(const char *from, const char *to, int uid, 
557                               int gid, struct externfs_data *ed)
558 {
559         struct humfs *mount = container_of(ed, struct humfs, ext);
560         struct humfs_file *hf;
561         const char *data_path[3] = { mount->data, from, NULL };
562         int err = -ENOMEM;
563
564         hf = (*mount->meta->init_file)();
565         if(hf == NULL)
566                 goto out;
567
568         err = (*mount->meta->create_file)(hf, from, S_IRWXUGO, uid, gid, NULL, 
569                                           mount);
570         if(err)
571                 goto out_close;
572
573         err = host_make_symlink(data_path, to);
574         if(err)
575                 (*mount->meta->remove_file)(from, mount);
576
577  out_close:
578         (*mount->meta->close_file)(hf);
579  out:
580         return(err);
581 }
582
583 static int humfs_link_file(const char *to, const char *from, int uid, int gid, 
584                            struct externfs_data *ed)
585 {
586         struct humfs *mount = container_of(ed, struct humfs, ext);
587         const char *data_path_from[3] = { mount->data, from, NULL };
588         const char *data_path_to[3] = { mount->data, to, NULL };
589         int err;
590
591         err = (*mount->meta->create_link)(to, from, mount);
592         if(err)
593                 return(err);
594
595         err = host_link_file(data_path_to, data_path_from);
596         if(err)
597                 (*mount->meta->remove_file)(from, mount);
598         
599         return(err);
600 }
601
602 static int humfs_unlink_file(const char *path, struct externfs_data *ed)
603 {
604         struct humfs *mount = container_of(ed, struct humfs, ext);
605         const char *data_path[3] = { mount->data, path, NULL };
606         int err;
607
608         err = (*mount->meta->remove_file)(path, mount);
609         if (err)
610                 return err;
611
612         (*mount->meta->remove_file)(path, mount);
613         return(host_unlink_file(data_path));
614 }
615
616 static void humfs_invisible(struct externfs_inode *ext)
617 {
618         struct humfs_file *hf = container_of(ext, struct humfs_file, ext);
619         struct humfs *mount = hf->mount;
620         
621         (*mount->meta->invisible)(hf);
622         not_reclaimable(&hf->data);
623 }
624
625 static int humfs_make_dir(const char *path, int mode, int uid, int gid, 
626                           struct externfs_data *ed)
627 {
628         struct humfs *mount = container_of(ed, struct humfs, ext);
629         const char *data_path[3] = { mount->data, path, NULL };
630         int err;
631
632         err = (*mount->meta->create_dir)(path, mode, uid, gid, mount);
633         if(err)
634                 return(err);
635         
636         err = host_make_dir(data_path, S_IRWXUGO);
637         if(err)
638                 (*mount->meta->remove_dir)(path, mount);
639
640         return(err);
641 }
642
643 static int humfs_remove_dir(const char *path, int uid, int gid, 
644                             struct externfs_data *ed)
645 {
646         struct humfs *mount = container_of(ed, struct humfs, ext);
647         const char *data_path[3] = { mount->data, path, NULL };
648         int err;
649
650         err = host_remove_dir(data_path);
651         if (err)
652                 return err;
653
654         (*mount->meta->remove_dir)(path, mount);
655
656         return(err);
657 }
658
659 static int humfs_read_link(char *file, int uid, int gid, char *buf, int size, 
660                            struct externfs_data *ed)
661 {
662         struct humfs *mount = container_of(ed, struct humfs, ext);
663         const char *data_path[3] = { mount->data, file, NULL };
664
665         return(host_read_link(data_path, buf, size));
666 }
667
668 struct humfs *inode_humfs_info(struct inode *inode)
669 {
670         return(container_of(inode_externfs_info(inode), struct humfs, ext));
671 }
672
673 static int humfs_rename_file(char *from, char *to, struct externfs_data *ed)
674 {
675         struct humfs *mount = container_of(ed, struct humfs, ext);
676         const char *data_path_from[3] = { mount->data, from, NULL };
677         const char *data_path_to[3] = { mount->data, to, NULL };
678         int err;
679
680         err = (*mount->meta->rename_file)(from, to, mount);
681         if(err)
682                 return(err);
683         
684         err = host_rename_file(data_path_from, data_path_to);
685         if(err)
686                 (*mount->meta->rename_file)(to, from, mount);
687
688         return(err);
689 }
690
691 static int humfs_stat_fs(long *bsize_out, long long *blocks_out, 
692                          long long *bfree_out, long long *bavail_out, 
693                          long long *files_out, long long *ffree_out, 
694                          void *fsid_out, int fsid_size, long *namelen_out, 
695                          long *spare_out, struct externfs_data *ed)
696 {
697         struct humfs *mount = container_of(ed, struct humfs, ext);
698         const char *data_path[3] = { mount->data, NULL };
699         int err;
700
701         /* XXX Needs to maintain this info as metadata */
702         err = host_stat_fs(data_path, bsize_out, blocks_out, bfree_out, 
703                            bavail_out, files_out, ffree_out, fsid_out, 
704                            fsid_size, namelen_out, spare_out);
705         if(err)
706                 return(err);
707
708         *blocks_out = mount->total / *bsize_out;
709         *bfree_out = (mount->total - mount->used) / *bsize_out;
710         *bavail_out = (mount->total - mount->used) / *bsize_out;
711         return(0);
712 }
713
714 int humfs_truncate_file(struct externfs_inode *ext, __u64 size, 
715                         struct externfs_data *ed)
716 {
717         struct humfs_file *hf = container_of(ext, struct humfs_file, ext);
718
719         return(truncate_file(&hf->data, size));
720 }
721
722 char *humfs_path(char *dir, char *file)
723 {
724         int need_slash, len = strlen(dir) + strlen(file);
725         char *new;
726
727         need_slash = (dir[strlen(dir) - 1] != '/');
728         if(need_slash)
729                 len++;
730
731         new = kmalloc(len + 1, GFP_KERNEL);
732         if(new == NULL)
733                 return(NULL);
734
735         strcpy(new, dir);
736         if(need_slash)
737                 strcat(new, "/");
738         strcat(new, file);
739
740         return(new);
741 }
742
743 DECLARE_MUTEX(meta_sem);
744 struct list_head metas = LIST_HEAD_INIT(metas);
745
746 static struct humfs_meta_ops *find_meta(const char *name)
747 {
748         struct list_head *ele;
749         struct humfs_meta_ops *m;
750  
751         down(&meta_sem);
752         list_for_each(ele, &metas){
753                 m = list_entry(ele, struct humfs_meta_ops, list);
754                 if(!strcmp(m->name, name))
755                         goto out;
756         }
757         m = NULL;
758  out:
759         up(&meta_sem);
760         return(m);
761 }
762
763 void register_meta(struct humfs_meta_ops *ops)
764 {
765         down(&meta_sem);
766         list_add(&ops->list, &metas);
767         up(&meta_sem);
768 }
769  
770 void unregister_meta(struct humfs_meta_ops *ops)
771 {
772         down(&meta_sem);
773         list_del(&ops->list);
774         up(&meta_sem);
775 }
776  
777 static struct humfs *read_superblock(char *root)
778 {
779         struct humfs *mount;
780         struct humfs_meta_ops *meta = NULL;
781         struct file_handle *fh;
782         const char *path[] = { root, "superblock", NULL };
783         u64 used, total;
784         char meta_buf[33], line[HOSTFS_BUFSIZE], *newline;
785         unsigned long long pos;
786         int version, i, n, err;
787
788         fh = kmalloc(sizeof(*fh), GFP_KERNEL);
789         if(fh == NULL)
790                 return(ERR_PTR(-ENOMEM));
791
792         err = host_open_file(path, 1, 0, fh);
793         if(err){
794                 printk("Failed to open %s/%s, errno = %d\n", path[0],
795                        path[1], err);
796                 return(ERR_PTR(err));
797         }
798
799         used = 0;
800         total = 0;
801         pos = 0;
802         i = 0;
803         while(1){
804                 n = read_file(fh, pos, &line[i], sizeof(line) - i - 1);
805                 if((n == 0) && (i == 0))
806                         break;
807                 if(n < 0)
808                         return(ERR_PTR(n));
809
810                 pos += n;
811                 if(n > 0)
812                         line[n + i] = '\0';
813
814                 newline = strchr(line, '\n');
815                 if(newline == NULL){
816                         printk("read_superblock - line too long : '%s'\n", 
817                                line);
818                         return(ERR_PTR(-EINVAL));
819                 }
820                 newline++;
821
822                 if(sscanf(line, "version %d\n", &version) == 1){
823                         if(version != HUMFS_VERSION){
824                                 printk("humfs version mismatch - want version "
825                                        "%d, got version %d.\n", HUMFS_VERSION,
826                                        version);
827                                 return(ERR_PTR(-EINVAL));
828                         }
829                 }
830                 else if(sscanf(line, "used %Lu\n", &used) == 1) ;
831                 else if(sscanf(line, "total %Lu\n", &total) == 1) ;
832                 else if(sscanf(line, "metadata %32s\n", meta_buf) == 1){
833                         meta = find_meta(meta_buf);
834                         if(meta == NULL){
835                                 printk("read_superblock - meta api \"%s\" not "
836                                        "registered\n", meta_buf);
837                                 return(ERR_PTR(-EINVAL));
838                         }
839                 }
840                 
841                 else {
842                         printk("read_superblock - bogus line : '%s'\n", line);
843                         return(ERR_PTR(-EINVAL));
844                 }
845
846                 i = newline - line;
847                 memmove(line, newline, sizeof(line) - i);
848                 i = strlen(line);
849         }
850
851         if(used == 0){
852                 printk("read_superblock - used not specified or set to "
853                        "zero\n");
854                 return(ERR_PTR(-EINVAL));
855         }
856         if(total == 0){
857                 printk("read_superblock - total not specified or set to "
858                        "zero\n");
859                 return(ERR_PTR(-EINVAL));
860         }
861         if(used > total){
862                 printk("read_superblock - used is greater than total\n");
863                 return(ERR_PTR(-EINVAL));
864         }
865
866         if(meta == NULL){
867                 meta = find_meta("shadow_fs");
868         }
869
870         if(meta == NULL){
871                 printk("read_superblock - valid meta api was not specified\n");
872                 return(ERR_PTR(-EINVAL));
873         }
874
875         mount = (*meta->init_mount)(root);
876         if(IS_ERR(mount))
877                 return(mount);
878
879         *mount = ((struct humfs) { .total       = total,
880                                    .used        = used,
881                                    .meta        = meta });
882         return(mount);
883 }
884
885 struct externfs_file_ops humfs_no_mmap_file_ops = {
886         .stat_file              = humfs_stat_file,
887         .file_type              = humfs_file_type,
888         .access_file            = NULL,
889         .open_file              = humfs_open_file,
890         .open_dir               = humfs_open_dir,
891         .read_dir               = humfs_read_dir,
892         .read_file              = humfs_read_file,
893         .write_file             = humfs_write_file,
894         .map_file_page          = NULL,
895         .close_file             = humfs_close_file,
896         .close_dir              = humfs_close_dir,
897         .invisible              = humfs_invisible,
898         .create_file            = humfs_create_file,
899         .set_attr               = humfs_set_attr,
900         .make_symlink           = humfs_make_symlink,
901         .unlink_file            = humfs_unlink_file,
902         .make_dir               = humfs_make_dir,
903         .remove_dir             = humfs_remove_dir,
904         .make_node              = humfs_make_node,
905         .link_file              = humfs_link_file,
906         .read_link              = humfs_read_link,
907         .rename_file            = humfs_rename_file,
908         .statfs                 = humfs_stat_fs,
909         .truncate_file          = humfs_truncate_file
910 };
911
912 struct externfs_file_ops humfs_mmap_file_ops = {
913         .stat_file              = humfs_stat_file,
914         .file_type              = humfs_file_type,
915         .access_file            = NULL,
916         .open_file              = humfs_open_file,
917         .open_dir               = humfs_open_dir,
918         .invisible              = humfs_invisible,
919         .read_dir               = humfs_read_dir,
920         .read_file              = humfs_read_file,
921         .write_file             = humfs_write_file,
922         .map_file_page          = humfs_map_file_page,
923         .close_file             = humfs_close_file,
924         .close_dir              = humfs_close_dir,
925         .create_file            = humfs_create_file,
926         .set_attr               = humfs_set_attr,
927         .make_symlink           = humfs_make_symlink,
928         .unlink_file            = humfs_unlink_file,
929         .make_dir               = humfs_make_dir,
930         .remove_dir             = humfs_remove_dir,
931         .make_node              = humfs_make_node,
932         .link_file              = humfs_link_file,
933         .read_link              = humfs_read_link,
934         .rename_file            = humfs_rename_file,
935         .statfs                 = humfs_stat_fs,
936         .truncate_file          = humfs_truncate_file
937 };
938
939 static struct externfs_data *mount_fs(char *mount_arg)
940 {
941         char *root, *data, *flags;
942         struct humfs *mount;
943         struct externfs_file_ops *file_ops;
944         int err, do_mmap = 0;
945
946         if(mount_arg == NULL){
947                 printk("humfs - no host directory specified\n");
948                 return(NULL);
949         }
950
951         flags = strchr((char *) mount_arg, ',');
952         if(flags != NULL){
953                 do {
954                         *flags++ = '\0';
955
956                         if(!strcmp(flags, "mmap"))
957                                 do_mmap = 1;
958
959                         flags = strchr(flags, ',');
960                 } while(flags != NULL);
961         }
962
963         err = -ENOMEM;
964         root = host_root_filename(mount_arg);
965         if(root == NULL)
966                 goto err;
967
968         mount = read_superblock(root);
969         if(IS_ERR(mount)){
970                 err = PTR_ERR(mount);
971                 goto err_free_root;
972         }
973
974         data = humfs_path(root, "data/");
975         if(data == NULL)
976                 goto err_free_mount;
977
978         if(CHOOSE_MODE(do_mmap, 0)){
979                 printk("humfs doesn't support mmap in tt mode\n");
980                 do_mmap = 0;
981         }
982
983         mount->data = data;
984         mount->mmap = do_mmap;
985
986         file_ops = do_mmap ? &humfs_mmap_file_ops : &humfs_no_mmap_file_ops;
987         init_externfs(&mount->ext, file_ops);
988
989         return(&mount->ext);
990
991  err_free_mount:
992         kfree(mount);
993  err_free_root:
994         kfree(root);
995  err:
996         return(NULL);
997 }
998
999 struct externfs_mount_ops humfs_mount_ops = {
1000         .init_file              = humfs_init_file,
1001         .mount                  = mount_fs,
1002 };
1003
1004 static int __init init_humfs(void)
1005 {
1006         return(register_externfs("humfs", &humfs_mount_ops));
1007 }
1008
1009 static void __exit exit_humfs(void)
1010 {
1011         unregister_externfs("humfs");
1012 }
1013
1014 __initcall(init_humfs);
1015 __exitcall(exit_humfs);
1016
1017 /*
1018  * Overrides for Emacs so that we follow Linus's tabbing style.
1019  * Emacs will notice this stuff at the end of the file and automatically
1020  * adjust the settings for this buffer only.  This must remain at the end
1021  * of the file.
1022  * ---------------------------------------------------------------------------
1023  * Local variables:
1024  * c-file-style: "linux"
1025  * End:
1026  */