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