File: /usr/src/linux/fs/umsdos/inode.c

1     /*
2      *  linux/fs/umsdos/inode.c
3      *
4      *      Written 1993 by Jacques Gelinas
5      *      Inspired from linux/fs/msdos/... by Werner Almesberger
6      */
7     
8     #include <linux/module.h>
9     
10     #include <linux/init.h>
11     #include <linux/fs.h>
12     #include <linux/msdos_fs.h>
13     #include <linux/kernel.h>
14     #include <linux/sched.h>
15     #include <linux/errno.h>
16     #include <asm/uaccess.h>
17     #include <linux/string.h>
18     #include <linux/stat.h>
19     #include <linux/umsdos_fs.h>
20     #include <linux/list.h>
21     #include <linux/pagemap.h>
22     
23     extern struct dentry_operations umsdos_dentry_operations;
24     
25     struct dentry *saved_root;	/* Original root if changed */
26     struct inode *pseudo_root;	/* Useful to simulate the pseudo DOS */
27     					/* directory. See UMSDOS_readdir_x() */
28     
29     static struct dentry *check_pseudo_root(struct super_block *);
30     
31     
32     void UMSDOS_put_inode (struct inode *inode)
33     {
34     	PRINTK ((KERN_DEBUG 
35     		"put inode %p (%lu) pos %lu count=%d\n"
36     		 ,inode, inode->i_ino
37     		 ,inode->u.umsdos_i.pos
38     		 ,atomic_read(&inode->i_count)));
39     
40     	if (inode == pseudo_root) {
41     		Printk ((KERN_ERR "Umsdos: debug: releasing pseudo_root - ino=%lu count=%d\n", inode->i_ino, atomic_read(&inode->i_count)));
42     	}
43     
44     	if (atomic_read(&inode->i_count) == 1)
45     		inode->u.umsdos_i.i_patched = 0;
46     }
47     
48     
49     void UMSDOS_put_super (struct super_block *sb)
50     {
51     	Printk ((KERN_DEBUG "UMSDOS_put_super: entering\n"));
52     	if (saved_root && pseudo_root && sb->s_dev == ROOT_DEV) {
53     		shrink_dcache_parent(saved_root);
54     		dput(saved_root);
55     		saved_root = NULL;
56     		pseudo_root = NULL;
57     	}
58     	msdos_put_super (sb);
59     }
60     
61     
62     /*
63      * Complete the setup of a directory dentry based on its
64      * EMD/non-EMD status.  If it has an EMD, then plug the
65      * umsdos function table. If not, use the msdos one.
66      */
67     void umsdos_setup_dir(struct dentry *dir)
68     {
69     	struct inode *inode = dir->d_inode;
70     
71     	if (!S_ISDIR(inode->i_mode))
72     		printk(KERN_ERR "umsdos_setup_dir: %s/%s not a dir!\n",
73     			dir->d_parent->d_name.name, dir->d_name.name);
74     
75     	init_waitqueue_head (&inode->u.umsdos_i.dir_info.p);
76     	inode->u.umsdos_i.dir_info.looking = 0;
77     	inode->u.umsdos_i.dir_info.creating = 0;
78     	inode->u.umsdos_i.dir_info.pid = 0;
79     
80     	inode->i_op = &umsdos_rdir_inode_operations;
81     	inode->i_fop = &umsdos_rdir_operations;
82     	if (umsdos_have_emd(dir)) {
83     Printk((KERN_DEBUG "umsdos_setup_dir: %s/%s using EMD\n",
84     dir->d_parent->d_name.name, dir->d_name.name));
85     		inode->i_op = &umsdos_dir_inode_operations;
86     		inode->i_fop = &umsdos_dir_operations;
87     	}
88     }
89     
90     
91     /*
92      * Add some info into an inode so it can find its owner quickly
93      */
94     void umsdos_set_dirinfo_new (struct dentry *dentry, off_t f_pos)
95     {
96     	struct inode *inode = dentry->d_inode;
97     	struct dentry *demd;
98     
99     	inode->u.umsdos_i.pos = f_pos;
100     
101     	/* now check the EMD file */
102     	demd = umsdos_get_emd_dentry(dentry->d_parent);
103     	if (!IS_ERR(demd)) {
104     		dput(demd);
105     	}
106     	return;
107     }
108     
109     static struct inode_operations umsdos_file_inode_operations = {
110     	truncate:	fat_truncate,
111     	setattr:	UMSDOS_notify_change,
112     };
113     
114     static struct inode_operations umsdos_symlink_inode_operations = {
115     	readlink:	page_readlink,
116     	follow_link:	page_follow_link,
117     	setattr:	UMSDOS_notify_change,
118     };
119     
120     /*
121      * Connect the proper tables in the inode and add some info.
122      */
123     /* #Specification: inode / umsdos info
124      * The first time an inode is seen (inode->i_count == 1),
125      * the inode number of the EMD file which controls this inode
126      * is tagged to this inode. It allows operations such as
127      * notify_change to be handled.
128      */
129     void umsdos_patch_dentry_inode(struct dentry *dentry, off_t f_pos)
130     {
131     	struct inode *inode = dentry->d_inode;
132     
133     PRINTK (("umsdos_patch_dentry_inode: inode=%lu\n", inode->i_ino));
134     
135     	/*
136     	 * Classify the inode based on EMD/non-EMD status.
137     	 */
138     PRINTK (("umsdos_patch_inode: call umsdos_set_dirinfo_new(%p,%lu)\n",
139     dentry, f_pos));
140     	umsdos_set_dirinfo_new(dentry, f_pos);
141     
142     	inode->i_op = &umsdos_file_inode_operations;
143     	if (S_ISREG (inode->i_mode)) {
144     		/* address_space operations already set */
145     	} else if (S_ISDIR (inode->i_mode)) {
146     		umsdos_setup_dir(dentry);
147     	} else if (S_ISLNK (inode->i_mode)) {
148     		/* address_space operations already set */
149     		inode->i_op = &umsdos_symlink_inode_operations;
150     	} else
151     		init_special_inode(inode, inode->i_mode,
152     					kdev_t_to_nr(inode->i_rdev));
153     }
154     
155     
156     /*
157      * lock the parent dir before starting ...
158      * also handles hardlink converting
159      */
160     int UMSDOS_notify_change (struct dentry *dentry, struct iattr *attr)
161     {
162     	struct inode *dir, *inode;
163     	struct umsdos_info info;
164     	struct dentry *temp, *old_dentry = NULL;
165     	int ret;
166     
167     	ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len,
168     				&info);
169     	if (ret)
170     		goto out;
171     	ret = umsdos_findentry (dentry->d_parent, &info, 0);
172     	if (ret) {
173     printk("UMSDOS_notify_change: %s/%s not in EMD, ret=%d\n",
174     dentry->d_parent->d_name.name, dentry->d_name.name, ret);
175     		goto out;
176     	}
177     
178     	if (info.entry.flags & UMSDOS_HLINK) {
179     		/*
180     		 * In order to get the correct (real) inode, we just drop
181     		 * the original dentry.
182     		 */ 
183     		d_drop(dentry);
184     Printk(("UMSDOS_notify_change: hard link %s/%s, fake=%s\n",
185     dentry->d_parent->d_name.name, dentry->d_name.name, info.fake.fname));
186     	
187     		/* Do a real lookup to get the short name dentry */
188     		temp = umsdos_covered(dentry->d_parent, info.fake.fname,
189     						info.fake.len);
190     		ret = PTR_ERR(temp);
191     		if (IS_ERR(temp))
192     			goto out;
193     	
194     		/* now resolve the link ... */
195     		temp = umsdos_solve_hlink(temp);
196     		ret = PTR_ERR(temp);
197     		if (IS_ERR(temp))
198     			goto out;
199     		old_dentry = dentry;
200     		dentry = temp;	/* so umsdos_notify_change_locked will operate on that */
201     	}
202     
203     	dir = dentry->d_parent->d_inode;
204     	inode = dentry->d_inode;
205     
206     	ret = inode_change_ok (inode, attr);
207     	if (ret)
208     		goto out;
209     
210     	down(&dir->i_sem);
211     	ret = umsdos_notify_change_locked(dentry, attr);
212     	up(&dir->i_sem);
213     	if (ret == 0)
214     		ret = inode_setattr (inode, attr);
215     out:
216     	if (old_dentry)
217     		dput (dentry);	/* if we had to use fake dentry for hardlinks, dput() it now */
218     	return ret;
219     }
220     
221     
222     /*
223      * Must be called with the parent lock held.
224      */
225     int umsdos_notify_change_locked(struct dentry *dentry, struct iattr *attr)
226     {
227     	struct inode *inode = dentry->d_inode;
228     	struct dentry *demd;
229     	struct address_space *mapping;
230     	struct page *page;
231     	int ret = 0;
232     	struct umsdos_dirent *entry;
233     	int offs;
234     
235     Printk(("UMSDOS_notify_change: entering for %s/%s (%d)\n",
236     dentry->d_parent->d_name.name, dentry->d_name.name, inode->u.umsdos_i.i_patched));
237     
238     	if (inode->i_nlink == 0)
239     		goto out;
240     	if (inode->i_ino == UMSDOS_ROOT_INO)
241     		goto out;
242     
243     	/* get the EMD file dentry */
244     	demd = umsdos_get_emd_dentry(dentry->d_parent);
245     	ret = PTR_ERR(demd);
246     	if (IS_ERR(demd))
247     		goto out;
248     	ret = 0;
249     	/* don't do anything if directory is not promoted to umsdos yet */
250     	if (!demd->d_inode) { 
251     		Printk((KERN_DEBUG
252     			"UMSDOS_notify_change: no EMD file %s/%s\n",
253     			demd->d_parent->d_name.name, demd->d_name.name));
254     		goto out_dput;
255     	}
256     
257     	/* don't do anything if this is the EMD itself */
258     	if (inode == demd->d_inode)
259     		goto out_dput;
260     
261     	/* This inode is not a EMD file nor an inode used internally
262     	 * by MSDOS, so we can update its status.
263     	 * See emd.c
264     	 */
265     
266     	/* Read only the start of the entry since we don't touch the name */
267     	mapping = demd->d_inode->i_mapping;
268     	offs = inode->u.umsdos_i.pos & ~PAGE_CACHE_MASK;
269     	ret = -ENOMEM;
270     	page=grab_cache_page(mapping,inode->u.umsdos_i.pos>>PAGE_CACHE_SHIFT);
271     	if (!page)
272     		goto out_dput;
273     	ret=mapping->a_ops->prepare_write(NULL,page,offs,offs+UMSDOS_REC_SIZE);
274     	if (ret)
275     		goto out_unlock;
276     	entry = (struct umsdos_dirent *) (page_address(page) + offs);
277     	if (attr->ia_valid & ATTR_UID)
278     		entry->uid = cpu_to_le16(attr->ia_uid);
279     	if (attr->ia_valid & ATTR_GID)
280     		entry->gid = cpu_to_le16(attr->ia_gid);
281     	if (attr->ia_valid & ATTR_MODE)
282     		entry->mode = cpu_to_le16(attr->ia_mode);
283     	if (attr->ia_valid & ATTR_ATIME)
284     		entry->atime = cpu_to_le32(attr->ia_atime);
285     	if (attr->ia_valid & ATTR_MTIME)
286     		entry->mtime = cpu_to_le32(attr->ia_mtime);
287     	if (attr->ia_valid & ATTR_CTIME)
288     		entry->ctime = cpu_to_le32(attr->ia_ctime);
289     	entry->nlink = cpu_to_le16(inode->i_nlink);
290     	ret=mapping->a_ops->commit_write(NULL,page,offs,offs+UMSDOS_REC_SIZE);
291     	if (ret)
292     		printk(KERN_WARNING
293     			"umsdos_notify_change: %s/%s EMD write error, ret=%d\n",
294     			dentry->d_parent->d_name.name, dentry->d_name.name,ret);
295     
296     	/* #Specification: notify_change / msdos fs
297     	 * notify_change operation are done only on the
298     	 * EMD file. The msdos fs is not even called.
299     	 */
300     out_unlock:
301     	UnlockPage(page);
302     	page_cache_release(page);
303     out_dput:
304     	dput(demd);
305     out:
306     	return ret;
307     }
308     
309     
310     /*
311      * Update the disk with the inode content
312      */
313     void UMSDOS_write_inode (struct inode *inode, int wait)
314     {
315     	struct iattr newattrs;
316     
317     	fat_write_inode (inode, wait);
318     	newattrs.ia_mtime = inode->i_mtime;
319     	newattrs.ia_atime = inode->i_atime;
320     	newattrs.ia_ctime = inode->i_ctime;
321     	newattrs.ia_valid = ATTR_MTIME | ATTR_ATIME | ATTR_CTIME;
322     	/*
323     	 * UMSDOS_notify_change is convenient to call here
324     	 * to update the EMD entry associated with this inode.
325     	 * But it has the side effect to re"dirt" the inode.
326     	 */
327     /*      
328      * UMSDOS_notify_change (inode, &newattrs);
329     
330      * inode->i_state &= ~I_DIRTY; / * FIXME: this doesn't work.  We need to remove ourselves from list on dirty inodes. /mn/ */
331     }
332     
333     
334     static struct super_operations umsdos_sops =
335     {
336     	write_inode:	UMSDOS_write_inode,
337     	put_inode:	UMSDOS_put_inode,
338     	delete_inode:	fat_delete_inode,
339     	put_super:	UMSDOS_put_super,
340     	statfs:		UMSDOS_statfs,
341     	clear_inode:	fat_clear_inode,
342     };
343     
344     int UMSDOS_statfs(struct super_block *sb,struct statfs *buf)
345     {
346     	int ret;
347     	ret = fat_statfs (sb, buf);
348     	if (!ret)	
349     		buf->f_namelen = UMSDOS_MAXNAME;
350     	return ret;
351     }
352     
353     /*
354      * Read the super block of an Extended MS-DOS FS.
355      */
356     struct super_block *UMSDOS_read_super (struct super_block *sb, void *data,
357     				      int silent)
358     {
359     	struct super_block *res;
360     	struct dentry *new_root;
361     
362     	/*
363     	 * Call msdos-fs to mount the disk.
364     	 * Note: this returns res == sb or NULL
365     	 */
366     	res = msdos_read_super (sb, data, silent);
367     
368     	if (!res)
369     		goto out_fail;
370     
371     	printk (KERN_INFO "UMSDOS 0.86k "
372     		"(compatibility level %d.%d, fast msdos)\n", 
373     		UMSDOS_VERSION, UMSDOS_RELEASE);
374     
375     	sb->s_op = &umsdos_sops;
376     	MSDOS_SB(sb)->options.dotsOK = 0;	/* disable hidden==dotfile */
377     
378     	/* install our dentry operations ... */
379     	sb->s_root->d_op = &umsdos_dentry_operations;
380     
381     	umsdos_patch_dentry_inode(sb->s_root, 0);
382     
383     	/* Check whether to change to the /linux root */
384     	new_root = check_pseudo_root(sb);
385     
386     	if (new_root) {
387     		/* sanity check */
388     		if (new_root->d_op != &umsdos_dentry_operations)
389     			printk("umsdos_read_super: pseudo-root wrong ops!\n");
390     
391     		pseudo_root = new_root->d_inode;
392     		saved_root = sb->s_root;
393     		printk(KERN_INFO "UMSDOS: changed to alternate root\n");
394     		dget (sb->s_root); sb->s_root = dget(new_root);
395     	}
396     	return sb;
397     
398     out_fail:
399     	printk(KERN_INFO "UMSDOS: msdos_read_super failed, mount aborted.\n");
400     	return NULL;
401     }
402     
403     /*
404      * Check for an alternate root if we're the root device.
405      */
406     
407     extern kdev_t ROOT_DEV;
408     static struct dentry *check_pseudo_root(struct super_block *sb)
409     {
410     	struct dentry *root, *sbin, *init;
411     
412     	/*
413     	 * Check whether we're mounted as the root device.
414     	 * must check like this, because we can be used with initrd
415     	 */
416     		
417     	if (sb->s_dev != ROOT_DEV)
418     		goto out_noroot;
419     
420     	/* 
421     	 * lookup_dentry needs a (so far non-existent) root. 
422     	 */
423     	printk(KERN_INFO "check_pseudo_root: mounted as root\n");
424     	root = lookup_one_len(UMSDOS_PSDROOT_NAME, sb->s_root,UMSDOS_PSDROOT_LEN); 
425     	if (IS_ERR(root))
426     		goto out_noroot;
427     		
428     	if (!root->d_inode || !S_ISDIR(root->d_inode->i_mode))
429     		goto out_dput;
430     
431     printk(KERN_INFO "check_pseudo_root: found %s/%s\n",
432     root->d_parent->d_name.name, root->d_name.name);
433     
434     	/* look for /sbin/init */
435     	sbin = lookup_one_len("sbin", root, 4);
436     	if (IS_ERR(sbin))
437     		goto out_dput;
438     	if (!sbin->d_inode || !S_ISDIR(sbin->d_inode->i_mode))
439     		goto out_dput_sbin;
440     	init = lookup_one_len("init", sbin, 4);
441     	if (IS_ERR(init))
442     		goto out_dput_sbin;
443     	if (!init->d_inode)
444     		goto out_dput_init;
445     	printk(KERN_INFO "check_pseudo_root: found %s/%s, enabling pseudo-root\n", init->d_parent->d_name.name, init->d_name.name);
446     	dput(sbin);
447     	dput(init);
448     	return root;
449     
450     	/* Alternate root not found ... */
451     out_dput_init:
452     	dput(init);
453     out_dput_sbin:
454     	dput(sbin);
455     out_dput:
456     	dput(root);
457     out_noroot:
458     	return NULL;
459     }
460     
461     
462     static DECLARE_FSTYPE_DEV(umsdos_fs_type, "umsdos", UMSDOS_read_super);
463     
464     static int __init init_umsdos_fs (void)
465     {
466     	return register_filesystem (&umsdos_fs_type);
467     }
468     
469     static void __exit exit_umsdos_fs (void)
470     {
471     	unregister_filesystem (&umsdos_fs_type);
472     }
473     
474     EXPORT_NO_SYMBOLS;
475     
476     module_init(init_umsdos_fs)
477     module_exit(exit_umsdos_fs)
478