2 * linux/fs/umsdos/inode.c
4 * Written 1993 by Jacques Gelinas
5 * Inspired from linux/fs/msdos/... by Werner Almesberger
8 #include <linux/module.h>
10 #include <linux/init.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>
23 extern struct dentry_operations umsdos_dentry_operations;
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() */
29 static struct dentry *check_pseudo_root(struct super_block *);
32 void UMSDOS_put_inode (struct inode *inode)
35 "put inode %p (%lu) pos %lu count=%d\n"
37 ,inode->u.umsdos_i.pos
38 ,atomic_read(&inode->i_count)));
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)));
44 if (atomic_read(&inode->i_count) == 1)
45 inode->u.umsdos_i.i_patched = 0;
49 void UMSDOS_put_super (struct super_block *sb)
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);
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.
67 void umsdos_setup_dir(struct dentry *dir)
69 struct inode *inode = dir->d_inode;
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);
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;
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;
92 * Add some info into an inode so it can find its owner quickly
94 void umsdos_set_dirinfo_new (struct dentry *dentry, off_t f_pos)
96 struct inode *inode = dentry->d_inode;
99 inode->u.umsdos_i.pos = f_pos;
101 /* now check the EMD file */
102 demd = umsdos_get_emd_dentry(dentry->d_parent);
109 static struct inode_operations umsdos_file_inode_operations = {
110 truncate: fat_truncate,
111 setattr: UMSDOS_notify_change,
114 static struct inode_operations umsdos_symlink_inode_operations = {
115 readlink: page_readlink,
116 follow_link: page_follow_link,
117 setattr: UMSDOS_notify_change,
121 * Connect the proper tables in the inode and add some info.
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.
129 void umsdos_patch_dentry_inode(struct dentry *dentry, off_t f_pos)
131 struct inode *inode = dentry->d_inode;
133 PRINTK (("umsdos_patch_dentry_inode: inode=%lu\n", inode->i_ino));
136 * Classify the inode based on EMD/non-EMD status.
138 PRINTK (("umsdos_patch_inode: call umsdos_set_dirinfo_new(%p,%lu)\n",
140 umsdos_set_dirinfo_new(dentry, f_pos);
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;
151 init_special_inode(inode, inode->i_mode,
152 kdev_t_to_nr(inode->i_rdev));
157 * lock the parent dir before starting ...
158 * also handles hardlink converting
160 int UMSDOS_notify_change (struct dentry *dentry, struct iattr *attr)
162 struct inode *dir, *inode;
163 struct umsdos_info info;
164 struct dentry *temp, *old_dentry = NULL;
167 ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len,
171 ret = umsdos_findentry (dentry->d_parent, &info, 0);
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);
178 if (info.entry.flags & UMSDOS_HLINK) {
180 * In order to get the correct (real) inode, we just drop
181 * the original 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));
187 /* Do a real lookup to get the short name dentry */
188 temp = umsdos_covered(dentry->d_parent, info.fake.fname,
194 /* now resolve the link ... */
195 temp = umsdos_solve_hlink(temp);
200 dentry = temp; /* so umsdos_notify_change_locked will operate on that */
203 dir = dentry->d_parent->d_inode;
204 inode = dentry->d_inode;
206 ret = inode_change_ok (inode, attr);
211 ret = umsdos_notify_change_locked(dentry, attr);
214 ret = inode_setattr (inode, attr);
217 dput (dentry); /* if we had to use fake dentry for hardlinks, dput() it now */
223 * Must be called with the parent lock held.
225 int umsdos_notify_change_locked(struct dentry *dentry, struct iattr *attr)
227 struct inode *inode = dentry->d_inode;
229 struct address_space *mapping;
232 struct umsdos_dirent *entry;
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));
238 if (inode->i_nlink == 0)
240 if (inode->i_ino == UMSDOS_ROOT_INO)
243 /* get the EMD file dentry */
244 demd = umsdos_get_emd_dentry(dentry->d_parent);
249 /* don't do anything if directory is not promoted to umsdos yet */
250 if (!demd->d_inode) {
252 "UMSDOS_notify_change: no EMD file %s/%s\n",
253 demd->d_parent->d_name.name, demd->d_name.name));
257 /* don't do anything if this is the EMD itself */
258 if (inode == demd->d_inode)
261 /* This inode is not a EMD file nor an inode used internally
262 * by MSDOS, so we can update its status.
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;
270 page=grab_cache_page(mapping,inode->u.umsdos_i.pos>>PAGE_CACHE_SHIFT);
273 ret=mapping->a_ops->prepare_write(NULL,page,offs,offs+UMSDOS_REC_SIZE);
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);
293 "umsdos_notify_change: %s/%s EMD write error, ret=%d\n",
294 dentry->d_parent->d_name.name, dentry->d_name.name,ret);
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.
302 page_cache_release(page);
311 * Update the disk with the inode content
313 void UMSDOS_write_inode (struct inode *inode, int wait)
315 struct iattr newattrs;
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;
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.
328 * UMSDOS_notify_change (inode, &newattrs);
330 * inode->i_state &= ~I_DIRTY; / * FIXME: this doesn't work. We need to remove ourselves from list on dirty inodes. /mn/ */
334 static struct super_operations umsdos_sops =
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,
344 int UMSDOS_statfs(struct super_block *sb,struct statfs *buf)
347 ret = fat_statfs (sb, buf);
349 buf->f_namelen = UMSDOS_MAXNAME;
354 * Read the super block of an Extended MS-DOS FS.
356 struct super_block *UMSDOS_read_super (struct super_block *sb, void *data,
359 struct super_block *res;
360 struct dentry *new_root;
363 * Call msdos-fs to mount the disk.
364 * Note: this returns res == sb or NULL
366 res = msdos_read_super (sb, data, silent);
371 printk (KERN_INFO "UMSDOS 0.86k "
372 "(compatibility level %d.%d, fast msdos)\n",
373 UMSDOS_VERSION, UMSDOS_RELEASE);
375 sb->s_op = &umsdos_sops;
376 MSDOS_SB(sb)->options.dotsOK = 0; /* disable hidden==dotfile */
378 /* install our dentry operations ... */
379 sb->s_root->d_op = &umsdos_dentry_operations;
381 umsdos_patch_dentry_inode(sb->s_root, 0);
383 /* Check whether to change to the /linux root */
384 new_root = check_pseudo_root(sb);
388 if (new_root->d_op != &umsdos_dentry_operations)
389 printk("umsdos_read_super: pseudo-root wrong ops!\n");
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);
399 printk(KERN_INFO "UMSDOS: msdos_read_super failed, mount aborted.\n");
404 * Check for an alternate root if we're the root device.
407 extern kdev_t ROOT_DEV;
408 static struct dentry *check_pseudo_root(struct super_block *sb)
410 struct dentry *root, *sbin, *init;
413 * Check whether we're mounted as the root device.
414 * must check like this, because we can be used with initrd
417 if (sb->s_dev != ROOT_DEV)
421 * lookup_dentry needs a (so far non-existent) root.
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);
428 if (!root->d_inode || !S_ISDIR(root->d_inode->i_mode))
431 printk(KERN_INFO "check_pseudo_root: found %s/%s\n",
432 root->d_parent->d_name.name, root->d_name.name);
434 /* look for /sbin/init */
435 sbin = lookup_one_len("sbin", root, 4);
438 if (!sbin->d_inode || !S_ISDIR(sbin->d_inode->i_mode))
440 init = lookup_one_len("init", sbin, 4);
445 printk(KERN_INFO "check_pseudo_root: found %s/%s, enabling pseudo-root\n", init->d_parent->d_name.name, init->d_name.name);
450 /* Alternate root not found ... */
462 static DECLARE_FSTYPE_DEV(umsdos_fs_type, "umsdos", UMSDOS_read_super);
464 static int __init init_umsdos_fs (void)
466 return register_filesystem (&umsdos_fs_type);
469 static void __exit exit_umsdos_fs (void)
471 unregister_filesystem (&umsdos_fs_type);
476 module_init(init_umsdos_fs)
477 module_exit(exit_umsdos_fs)
478 MODULE_LICENSE("GPL");