File: /usr/src/linux/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 = bread(dev, 0, ROMBSIZE);
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 = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE);
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 = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE);
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 = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE);
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 = bread(i->i_dev, offset>>ROMBSBITS, ROMBSIZE);
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