import of upstream 2.4.34.4 from kernel.org
[linux-2.4.git] / fs / romfs / inode.c
1 /*
2  * ROMFS file system, Linux implementation
3  *
4  * Copyright (C) 1997-1999  Janos Farkas <chexum@shadow.banki.hu>
5  *
6  * Using parts of the minix filesystem
7  * Copyright (C) 1991, 1992  Linus Torvalds
8  *
9  * and parts of the affs filesystem additionally
10  * Copyright (C) 1993  Ray Burr
11  * Copyright (C) 1996  Hans-Joachim Widmaier
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version
16  * 2 of the License, or (at your option) any later version.
17  *
18  * Changes
19  *                                      Changed for 2.1.19 modules
20  *      Jan 1997                        Initial release
21  *      Jun 1997                        2.1.43+ changes
22  *                                      Proper page locking in readpage
23  *                                      Changed to work with 2.1.45+ fs
24  *      Jul 1997                        Fixed follow_link
25  *                      2.1.47
26  *                                      lookup shouldn't return -ENOENT
27  *                                      from Horst von Brand:
28  *                                        fail on wrong checksum
29  *                                        double unlock_super was possible
30  *                                        correct namelen for statfs
31  *                                      spotted by Bill Hawes:
32  *                                        readlink shouldn't iput()
33  *      Jun 1998        2.1.106         from Avery Pennarun: glibc scandir()
34  *                                        exposed a problem in readdir
35  *                      2.1.107         code-freeze spellchecker run
36  *      Aug 1998                        2.1.118+ VFS changes
37  *      Sep 1998        2.1.122         another VFS change (follow_link)
38  *      Apr 1999        2.2.7           no more EBADF checking in
39  *                                        lookup/readdir, use ERR_PTR
40  *      Jun 1999        2.3.6           d_alloc_root use changed
41  *                      2.3.9           clean up usage of ENOENT/negative
42  *                                        dentries in lookup
43  *                                      clean up page flags setting
44  *                                        (error, uptodate, locking) in
45  *                                        in readpage
46  *                                      use init_special_inode for
47  *                                        fifos/sockets (and streamline) in
48  *                                        read_inode, fix _ops table order
49  *      Aug 1999        2.3.16          __initfunc() => __init change
50  *      Oct 1999        2.3.24          page->owner hack obsoleted
51  *      Nov 1999        2.3.27          2.3.25+ page->offset => index change
52  */
53
54 /* todo:
55  *      - see Documentation/filesystems/romfs.txt
56  *      - use allocated, not stack memory for file names?
57  *      - considering write access...
58  *      - network (tftp) files?
59  *      - merge back some _op tables
60  */
61
62 /*
63  * Sorry about some optimizations and for some goto's.  I just wanted
64  * to squeeze some more bytes out of this code.. :)
65  */
66
67 #include <linux/module.h>
68 #include <linux/types.h>
69 #include <linux/errno.h>
70 #include <linux/slab.h>
71 #include <linux/romfs_fs.h>
72 #include <linux/fs.h>
73 #include <linux/locks.h>
74 #include <linux/init.h>
75 #include <linux/smp_lock.h>
76
77 #include <asm/uaccess.h>
78
79 static __s32
80 romfs_checksum(void *data, int size)
81 {
82         __s32 sum, *ptr;
83
84         sum = 0; ptr = data;
85         size>>=2;
86         while (size>0) {
87                 sum += ntohl(*ptr++);
88                 size--;
89         }
90         return sum;
91 }
92
93 static struct super_operations romfs_ops;
94
95 static struct super_block *
96 romfs_read_super(struct super_block *s, void *data, int silent)
97 {
98         struct buffer_head *bh;
99         kdev_t dev = s->s_dev;
100         struct romfs_super_block *rsb;
101         int sz;
102
103         /* I would parse the options here, but there are none.. :) */
104
105         set_blocksize(dev, ROMBSIZE);
106         s->s_blocksize = ROMBSIZE;
107         s->s_blocksize_bits = ROMBSBITS;
108         s->u.generic_sbp = (void *) 0;
109         s->s_maxbytes = 0xFFFFFFFF;
110
111         bh = sb_bread(s, 0);
112         if (!bh) {
113                 /* XXX merge with other printk? */
114                 printk ("romfs: unable to read superblock\n");
115                 goto outnobh;
116         }
117
118         rsb = (struct romfs_super_block *)bh->b_data;
119         sz = ntohl(rsb->size);
120         if (rsb->word0 != ROMSB_WORD0 || rsb->word1 != ROMSB_WORD1
121            || sz < ROMFH_SIZE) {
122                 if (!silent)
123                         printk ("VFS: Can't find a romfs filesystem on dev "
124                                 "%s.\n", kdevname(dev));
125                 goto out;
126         }
127         if (romfs_checksum(rsb, min_t(int, sz, 512))) {
128                 printk ("romfs: bad initial checksum on dev "
129                         "%s.\n", kdevname(dev));
130                 goto out;
131         }
132
133         s->s_magic = ROMFS_MAGIC;
134         s->u.romfs_sb.s_maxsize = sz;
135
136         s->s_flags |= MS_RDONLY;
137
138         /* Find the start of the fs */
139         sz = (ROMFH_SIZE +
140               strnlen(rsb->name, ROMFS_MAXFN) + 1 + ROMFH_PAD)
141              & ROMFH_MASK;
142
143         brelse(bh);
144
145         s->s_op = &romfs_ops;
146         s->s_root = d_alloc_root(iget(s, sz));
147
148         if (!s->s_root)
149                 goto outnobh;
150
151         /* Ehrhm; sorry.. :)  And thanks to Hans-Joachim Widmaier  :) */
152         if (0) {
153 out:
154                 brelse(bh);
155 outnobh:
156                 s = NULL;
157         }
158
159         return s;
160 }
161
162 /* That's simple too. */
163
164 static int
165 romfs_statfs(struct super_block *sb, struct statfs *buf)
166 {
167         buf->f_type = ROMFS_MAGIC;
168         buf->f_bsize = ROMBSIZE;
169         buf->f_bfree = buf->f_bavail = buf->f_ffree;
170         buf->f_blocks = (sb->u.romfs_sb.s_maxsize+ROMBSIZE-1)>>ROMBSBITS;
171         buf->f_namelen = ROMFS_MAXFN;
172         return 0;
173 }
174
175 /* some helper routines */
176
177 static int
178 romfs_strnlen(struct inode *i, unsigned long offset, unsigned long count)
179 {
180         struct buffer_head *bh;
181         unsigned long avail, maxsize, res;
182
183         maxsize = i->i_sb->u.romfs_sb.s_maxsize;
184         if (offset >= maxsize)
185                 return -1;
186
187         /* strnlen is almost always valid */
188         if (count > maxsize || offset+count > maxsize)
189                 count = maxsize-offset;
190
191         bh = sb_bread(i->i_sb, offset>>ROMBSBITS);
192         if (!bh)
193                 return -1;              /* error */
194
195         avail = ROMBSIZE - (offset & ROMBMASK);
196         maxsize = min_t(unsigned long, count, avail);
197         res = strnlen(((char *)bh->b_data)+(offset&ROMBMASK), maxsize);
198         brelse(bh);
199
200         if (res < maxsize)
201                 return res;             /* found all of it */
202
203         while (res < count) {
204                 offset += maxsize;
205
206                 bh = sb_bread(i->i_sb, offset>>ROMBSBITS);
207                 if (!bh)
208                         return -1;
209                 maxsize = min_t(unsigned long, count - res, ROMBSIZE);
210                 avail = strnlen(bh->b_data, maxsize);
211                 res += avail;
212                 brelse(bh);
213                 if (avail < maxsize)
214                         return res;
215         }
216         return res;
217 }
218
219 static int
220 romfs_copyfrom(struct inode *i, void *dest, unsigned long offset, unsigned long count)
221 {
222         struct buffer_head *bh;
223         unsigned long avail, maxsize, res;
224
225         maxsize = i->i_sb->u.romfs_sb.s_maxsize;
226         if (offset >= maxsize || count > maxsize || offset+count>maxsize)
227                 return -1;
228
229         bh = sb_bread(i->i_sb, offset>>ROMBSBITS);
230         if (!bh)
231                 return -1;              /* error */
232
233         avail = ROMBSIZE - (offset & ROMBMASK);
234         maxsize = min_t(unsigned long, count, avail);
235         memcpy(dest, ((char *)bh->b_data) + (offset & ROMBMASK), maxsize);
236         brelse(bh);
237
238         res = maxsize;                  /* all of it */
239
240         while (res < count) {
241                 offset += maxsize;
242                 dest += maxsize;
243
244                 bh = sb_bread(i->i_sb, offset>>ROMBSBITS);
245                 if (!bh)
246                         return -1;
247                 maxsize = min_t(unsigned long, count - res, ROMBSIZE);
248                 memcpy(dest, bh->b_data, maxsize);
249                 brelse(bh);
250                 res += maxsize;
251         }
252         return res;
253 }
254
255 static unsigned char romfs_dtype_table[] = {
256         DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_SOCK, DT_FIFO
257 };
258
259 static int
260 romfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
261 {
262         struct inode *i = filp->f_dentry->d_inode;
263         struct romfs_inode ri;
264         unsigned long offset, maxoff;
265         int j, ino, nextfh;
266         int stored = 0;
267         char fsname[ROMFS_MAXFN];       /* XXX dynamic? */
268
269         maxoff = i->i_sb->u.romfs_sb.s_maxsize;
270
271         offset = filp->f_pos;
272         if (!offset) {
273                 offset = i->i_ino & ROMFH_MASK;
274                 if (romfs_copyfrom(i, &ri, offset, ROMFH_SIZE) <= 0)
275                         return stored;
276                 offset = ntohl(ri.spec) & ROMFH_MASK;
277         }
278
279         /* Not really failsafe, but we are read-only... */
280         for(;;) {
281                 if (!offset || offset >= maxoff) {
282                         offset = maxoff;
283                         filp->f_pos = offset;
284                         return stored;
285                 }
286                 filp->f_pos = offset;
287
288                 /* Fetch inode info */
289                 if (romfs_copyfrom(i, &ri, offset, ROMFH_SIZE) <= 0)
290                         return stored;
291
292                 j = romfs_strnlen(i, offset+ROMFH_SIZE, sizeof(fsname)-1);
293                 if (j < 0)
294                         return stored;
295
296                 fsname[j]=0;
297                 romfs_copyfrom(i, fsname, offset+ROMFH_SIZE, j);
298
299                 ino = offset;
300                 nextfh = ntohl(ri.next);
301                 if ((nextfh & ROMFH_TYPE) == ROMFH_HRD)
302                         ino = ntohl(ri.spec);
303                 if (filldir(dirent, fsname, j, offset, ino,
304                             romfs_dtype_table[nextfh & ROMFH_TYPE]) < 0) {
305                         return stored;
306                 }
307                 stored++;
308                 offset = nextfh & ROMFH_MASK;
309         }
310 }
311
312 static struct dentry *
313 romfs_lookup(struct inode *dir, struct dentry *dentry)
314 {
315         unsigned long offset, maxoff;
316         int fslen, res;
317         struct inode *inode;
318         char fsname[ROMFS_MAXFN];       /* XXX dynamic? */
319         struct romfs_inode ri;
320         const char *name;               /* got from dentry */
321         int len;
322
323         res = -EACCES;                  /* placeholder for "no data here" */
324         offset = dir->i_ino & ROMFH_MASK;
325         if (romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0)
326                 goto out;
327
328         maxoff = dir->i_sb->u.romfs_sb.s_maxsize;
329         offset = ntohl(ri.spec) & ROMFH_MASK;
330
331         /* OK, now find the file whose name is in "dentry" in the
332          * directory specified by "dir".  */
333
334         name = dentry->d_name.name;
335         len = dentry->d_name.len;
336
337         for(;;) {
338                 if (!offset || offset >= maxoff)
339                         goto out0;
340                 if (romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0)
341                         goto out;
342
343                 /* try to match the first 16 bytes of name */
344                 fslen = romfs_strnlen(dir, offset+ROMFH_SIZE, ROMFH_SIZE);
345                 if (len < ROMFH_SIZE) {
346                         if (len == fslen) {
347                                 /* both are shorter, and same size */
348                                 romfs_copyfrom(dir, fsname, offset+ROMFH_SIZE, len+1);
349                                 if (strncmp (name, fsname, len) == 0)
350                                         break;
351                         }
352                 } else if (fslen >= ROMFH_SIZE) {
353                         /* both are longer; XXX optimize max size */
354                         fslen = romfs_strnlen(dir, offset+ROMFH_SIZE, sizeof(fsname)-1);
355                         if (len == fslen) {
356                                 romfs_copyfrom(dir, fsname, offset+ROMFH_SIZE, len+1);
357                                 if (strncmp(name, fsname, len) == 0)
358                                         break;
359                         }
360                 }
361                 /* next entry */
362                 offset = ntohl(ri.next) & ROMFH_MASK;
363         }
364
365         /* Hard link handling */
366         if ((ntohl(ri.next) & ROMFH_TYPE) == ROMFH_HRD)
367                 offset = ntohl(ri.spec) & ROMFH_MASK;
368
369         if ((inode = iget(dir->i_sb, offset)))
370                 goto outi;
371
372         /*
373          * it's a bit funky, _lookup needs to return an error code
374          * (negative) or a NULL, both as a dentry.  ENOENT should not
375          * be returned, instead we need to create a negative dentry by
376          * d_add(dentry, NULL); and return 0 as no error.
377          * (Although as I see, it only matters on writable file
378          * systems).
379          */
380
381 out0:   inode = NULL;
382 outi:   res = 0;
383         d_add (dentry, inode);
384
385 out:    return ERR_PTR(res);
386 }
387
388 /*
389  * Ok, we do readpage, to be able to execute programs.  Unfortunately,
390  * we can't use bmap, since we may have looser alignments.
391  */
392
393 static int
394 romfs_readpage(struct file *file, struct page * page)
395 {
396         struct inode *inode = page->mapping->host;
397         unsigned long offset, avail, readlen;
398         void *buf;
399         int result = -EIO;
400
401         page_cache_get(page);
402         lock_kernel();
403         buf = kmap(page);
404         if (!buf)
405                 goto err_out;
406
407         /* 32 bit warning -- but not for us :) */
408         offset = page->index << PAGE_CACHE_SHIFT;
409         if (offset < inode->i_size) {
410                 avail = inode->i_size-offset;
411                 readlen = min_t(unsigned long, avail, PAGE_SIZE);
412                 if (romfs_copyfrom(inode, buf, inode->u.romfs_i.i_dataoffset+offset, readlen) == readlen) {
413                         if (readlen < PAGE_SIZE) {
414                                 memset(buf + readlen,0,PAGE_SIZE-readlen);
415                         }
416                         SetPageUptodate(page);
417                         result = 0;
418                 }
419         }
420         if (result) {
421                 memset(buf, 0, PAGE_SIZE);
422                 SetPageError(page);
423         }
424         flush_dcache_page(page);
425
426         UnlockPage(page);
427
428         kunmap(page);
429 err_out:
430         page_cache_release(page);
431         unlock_kernel();
432
433         return result;
434 }
435
436 /* Mapping from our types to the kernel */
437
438 static struct address_space_operations romfs_aops = {
439         readpage: romfs_readpage
440 };
441
442 static struct file_operations romfs_dir_operations = {
443         read:           generic_read_dir,
444         readdir:        romfs_readdir,
445 };
446
447 static struct inode_operations romfs_dir_inode_operations = {
448         lookup:         romfs_lookup,
449 };
450
451 static mode_t romfs_modemap[] =
452 {
453         0, S_IFDIR+0644, S_IFREG+0644, S_IFLNK+0777,
454         S_IFBLK+0600, S_IFCHR+0600, S_IFSOCK+0644, S_IFIFO+0644
455 };
456
457 static void
458 romfs_read_inode(struct inode *i)
459 {
460         int nextfh, ino;
461         struct romfs_inode ri;
462
463         ino = i->i_ino & ROMFH_MASK;
464         i->i_mode = 0;
465
466         /* Loop for finding the real hard link */
467         for(;;) {
468                 if (romfs_copyfrom(i, &ri, ino, ROMFH_SIZE) <= 0) {
469                         printk("romfs: read error for inode 0x%x\n", ino);
470                         return;
471                 }
472                 /* XXX: do romfs_checksum here too (with name) */
473
474                 nextfh = ntohl(ri.next);
475                 if ((nextfh & ROMFH_TYPE) != ROMFH_HRD)
476                         break;
477
478                 ino = ntohl(ri.spec) & ROMFH_MASK;
479         }
480
481         i->i_nlink = 1;         /* Hard to decide.. */
482         i->i_size = ntohl(ri.size);
483         i->i_mtime = i->i_atime = i->i_ctime = 0;
484         i->i_uid = i->i_gid = 0;
485
486         /* Precalculate the data offset */
487         ino = romfs_strnlen(i, ino+ROMFH_SIZE, ROMFS_MAXFN);
488         if (ino >= 0)
489                 ino = ((ROMFH_SIZE+ino+1+ROMFH_PAD)&ROMFH_MASK);
490         else
491                 ino = 0;
492
493         i->u.romfs_i.i_metasize = ino;
494         i->u.romfs_i.i_dataoffset = ino+(i->i_ino&ROMFH_MASK);
495
496         /* Compute permissions */
497         ino = romfs_modemap[nextfh & ROMFH_TYPE];
498         /* only "normal" files have ops */
499         switch (nextfh & ROMFH_TYPE) {
500                 case 1:
501                         i->i_size = i->u.romfs_i.i_metasize;
502                         i->i_op = &romfs_dir_inode_operations;
503                         i->i_fop = &romfs_dir_operations;
504                         if (nextfh & ROMFH_EXEC)
505                                 ino |= S_IXUGO;
506                         i->i_mode = ino;
507                         break;
508                 case 2:
509                         i->i_fop = &generic_ro_fops;
510                         i->i_data.a_ops = &romfs_aops;
511                         if (nextfh & ROMFH_EXEC)
512                                 ino |= S_IXUGO;
513                         i->i_mode = ino;
514                         break;
515                 case 3:
516                         i->i_op = &page_symlink_inode_operations;
517                         i->i_data.a_ops = &romfs_aops;
518                         i->i_mode = ino | S_IRWXUGO;
519                         break;
520                 default:
521                         /* depending on MBZ for sock/fifos */
522                         nextfh = ntohl(ri.spec);
523                         nextfh = kdev_t_to_nr(MKDEV(nextfh>>16,nextfh&0xffff));
524                         init_special_inode(i, ino, nextfh);
525         }
526 }
527
528 static struct super_operations romfs_ops = {
529         read_inode:     romfs_read_inode,
530         statfs:         romfs_statfs,
531 };
532
533 static DECLARE_FSTYPE_DEV(romfs_fs_type, "romfs", romfs_read_super);
534
535 static int __init init_romfs_fs(void)
536 {
537         return register_filesystem(&romfs_fs_type);
538 }
539
540 static void __exit exit_romfs_fs(void)
541 {
542         unregister_filesystem(&romfs_fs_type);
543 }
544
545 /* Yes, works even as a module... :) */
546
547 EXPORT_NO_SYMBOLS;
548
549 module_init(init_romfs_fs)
550 module_exit(exit_romfs_fs)
551 MODULE_LICENSE("GPL");