ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / fs / umsdos / dir.c
1 /*
2  *  linux/fs/umsdos/dir.c
3  *
4  *  Written 1993 by Jacques Gelinas
5  *      Inspired from linux/fs/msdos/... : Werner Almesberger
6  *
7  *  Extended MS-DOS directory handling functions
8  */
9
10 #include <linux/time.h>
11 #include <linux/string.h>
12 #include <linux/fs.h>
13 #include <linux/msdos_fs.h>
14 #include <linux/errno.h>
15 #include <linux/stat.h>
16 #include <linux/limits.h>
17 #include <linux/umsdos_fs.h>
18 #include <linux/slab.h>
19 #include <linux/pagemap.h>
20 #include <linux/smp_lock.h>
21
22 #define UMSDOS_SPECIAL_DIRFPOS  3
23 extern struct dentry *saved_root;
24 extern struct inode *pseudo_root;
25
26 /* #define UMSDOS_DEBUG_VERBOSE 1 */
27
28 /*
29  * Dentry operations routines
30  */
31
32 /* nothing for now ... */
33 static int umsdos_dentry_validate(struct dentry *dentry, struct nameidata *nd)
34 {
35         return 1;
36 }
37
38 /* for now, drop everything to force lookups ... */
39 /* ITYM s/everything/& positive/... */
40 static int umsdos_dentry_dput(struct dentry *dentry)
41 {
42         struct inode *inode = dentry->d_inode;
43         if (inode) {
44                 return 1;
45         }
46         return 0;
47 }
48
49 struct dentry_operations umsdos_dentry_operations =
50 {
51         .d_revalidate   = umsdos_dentry_validate,
52         .d_delete       = umsdos_dentry_dput,
53 };
54
55 struct UMSDOS_DIR_ONCE {
56         void *dirbuf;
57         filldir_t filldir;
58         int count;
59         int stop;
60 };
61
62 /*
63  * Record a single entry the first call.
64  * Return -EINVAL the next one.
65  * NOTE: filldir DOES NOT use a dentry
66  */
67
68 static int umsdos_dir_once (    void *buf,
69                                 const char *name,
70                                 int len,
71                                 loff_t offset,
72                                 ino_t ino,
73                                 unsigned type)
74 {
75         int ret = -EINVAL;
76         struct UMSDOS_DIR_ONCE *d = (struct UMSDOS_DIR_ONCE *) buf;
77
78         if (d->count == 0) {
79                 PRINTK ((KERN_DEBUG "dir_once :%.*s: offset %Ld\n", 
80                         len, name, offset));
81                 ret = d->filldir (d->dirbuf, name, len, offset, ino, DT_UNKNOWN);
82                 d->stop = ret < 0;
83                 d->count = 1;
84         }
85         return ret;
86 }
87
88
89 /*
90  * Read count directory entries from directory filp
91  * Return a negative value from linux/errno.h.
92  * Return > 0 if success (the number of bytes written by filldir).
93  * 
94  * This function is used by the normal readdir VFS entry point,
95  * and in order to get the directory entry from a file's dentry.
96  * See umsdos_dentry_to_entry() below.
97  */
98  
99 static int umsdos_readdir_x (struct inode *dir, struct file *filp,
100                                 void *dirbuf, struct umsdos_dirent *u_entry,
101                                 filldir_t filldir)
102 {
103         struct dentry *demd;
104         off_t start_fpos;
105         int ret = 0;
106         loff_t pos;
107
108         umsdos_startlookup (dir);
109
110         if (filp->f_pos == UMSDOS_SPECIAL_DIRFPOS && dir == pseudo_root) {
111
112                 /*
113                  * We don't need to simulate this pseudo directory
114                  * when umsdos_readdir_x is called for internal operation
115                  * of umsdos. This is why dirent_in_fs is tested
116                  */
117                 /* #Specification: pseudo root / directory /DOS
118                  * When umsdos operates in pseudo root mode (C:\linux is the
119                  * linux root), it simulate a directory /DOS which points to
120                  * the real root of the file system.
121                  */
122
123                 Printk ((KERN_WARNING "umsdos_readdir_x: pseudo_root thing UMSDOS_SPECIAL_DIRFPOS\n"));
124                 if (filldir (dirbuf, "DOS", 3, 
125                                 UMSDOS_SPECIAL_DIRFPOS, UMSDOS_ROOT_INO, DT_DIR) == 0) {
126                         filp->f_pos++;
127                 }
128                 goto out_end;
129         }
130
131         if (filp->f_pos < 2 || 
132             (dir->i_ino != UMSDOS_ROOT_INO && filp->f_pos == 32)) {
133         
134                 int last_f_pos = filp->f_pos;
135                 struct UMSDOS_DIR_ONCE bufk;
136
137                 Printk (("umsdos_readdir_x: . or .. /mn/?\n"));
138
139                 bufk.dirbuf = dirbuf;
140                 bufk.filldir = filldir;
141                 bufk.count = 0;
142
143                 ret = fat_readdir (filp, &bufk, umsdos_dir_once);
144                 if (last_f_pos > 0 && filp->f_pos > last_f_pos)
145                         filp->f_pos = UMSDOS_SPECIAL_DIRFPOS;
146                 if (u_entry != NULL)
147                         u_entry->flags = 0;
148                 goto out_end;
149         }
150
151         Printk (("umsdos_readdir_x: normal file /mn/?\n"));
152
153         /* get the EMD dentry */
154         demd = umsdos_get_emd_dentry(filp->f_dentry);
155         ret = PTR_ERR(demd);
156         if (IS_ERR(demd))
157                 goto out_end;
158         ret = -EIO;
159         if (!demd->d_inode) {
160                 printk(KERN_WARNING 
161                         "umsdos_readir_x: EMD file %s/%s not found\n",
162                         demd->d_parent->d_name.name, demd->d_name.name);
163                 goto out_dput;
164         }
165
166         pos = filp->f_pos;
167         start_fpos = filp->f_pos;
168
169         if (pos <= UMSDOS_SPECIAL_DIRFPOS + 1)
170                 pos = 0;
171         ret = 0;
172         while (pos < demd->d_inode->i_size) {
173                 off_t cur_f_pos = pos;
174                 struct dentry *dret;
175                 struct inode *inode;
176                 struct umsdos_dirent entry;
177                 struct umsdos_info info;
178
179                 ret = -EIO;
180                 if (umsdos_emd_dir_readentry (demd, &pos, &entry) != 0)
181                         break;
182                 if (entry.name_len == 0)
183                         continue;
184 #ifdef UMSDOS_DEBUG_VERBOSE
185 if (entry.flags & UMSDOS_HLINK)
186 printk("umsdos_readdir_x: %s/%s is hardlink\n",
187 filp->f_dentry->d_name.name, entry.name);
188 #endif
189
190                 umsdos_parse (entry.name, entry.name_len, &info);
191                 info.f_pos = cur_f_pos;
192                 umsdos_manglename (&info);
193                 /*
194                  * Do a real lookup on the short name.
195                  */
196                 dret = umsdos_covered(filp->f_dentry, info.fake.fname,
197                                                  info.fake.len);
198                 ret = PTR_ERR(dret);
199                 if (IS_ERR(dret))
200                         break;
201                 /*
202                  * If the file wasn't found, remove it from the EMD.
203                  */
204                 inode = dret->d_inode;
205                 if (!inode)
206                         goto remove_name;
207 #ifdef UMSDOS_DEBUG_VERBOSE
208 if (UMSDOS_I(inode)->i_is_hlink)
209 printk("umsdos_readdir_x: %s/%s already resolved, ino=%ld\n",
210 dret->d_parent->d_name.name, dret->d_name.name, inode->i_ino);
211 #endif
212
213 Printk (("Found %s/%s, ino=%ld, flags=%x\n",
214 dret->d_parent->d_name.name, info.fake.fname, dret->d_inode->i_ino,
215 entry.flags));
216                 /* check whether to resolve a hard-link */
217                 if ((entry.flags & UMSDOS_HLINK) &&
218                     !UMSDOS_I(inode)->i_is_hlink) {
219                         dret = umsdos_solve_hlink (dret);
220                         ret = PTR_ERR(dret);
221                         if (IS_ERR(dret))
222                                 break;
223                         inode = dret->d_inode;
224                         if (!inode) {
225 printk("umsdos_readdir_x: %s/%s negative after link\n",
226 dret->d_parent->d_name.name, dret->d_name.name);
227                                 goto clean_up;
228                         }
229                 }
230
231                 /* #Specification:  pseudo root / reading real root
232                  * The pseudo root (/linux) is logically
233                  * erased from the real root.  This means that
234                  * ls /DOS, won't show "linux". This avoids
235                  * infinite recursion (/DOS/linux/DOS/linux/...) while
236                  * walking the file system.
237                  */
238                 if (inode != pseudo_root && !(entry.flags & UMSDOS_HIDDEN)) {
239                         if (filldir (dirbuf, entry.name, entry.name_len,
240                                  cur_f_pos, inode->i_ino, DT_UNKNOWN) < 0) {
241                                 pos = cur_f_pos;
242                         }
243 Printk(("umsdos_readdir_x: got %s/%s, ino=%ld\n",
244 dret->d_parent->d_name.name, dret->d_name.name, inode->i_ino));
245                         if (u_entry != NULL)
246                                 *u_entry = entry;
247                         dput(dret);
248                         ret = 0;
249                         break;
250                 }
251         clean_up:
252                 dput(dret);
253                 continue;
254
255         remove_name:
256                 /* #Specification:  umsdos / readdir / not in MSDOS
257                  * During a readdir operation, if the file is not
258                  * in the MS-DOS directory any more, the entry is
259                  * removed from the EMD file silently.
260                  */
261 #ifdef UMSDOS_PARANOIA
262 printk("umsdos_readdir_x: %s/%s out of sync, erasing\n",
263 filp->f_dentry->d_name.name, info.entry.name);
264 #endif
265                 ret = umsdos_delentry(filp->f_dentry, &info, 
266                                         S_ISDIR(info.entry.mode));
267                 if (ret)
268                         printk(KERN_WARNING 
269                                 "umsdos_readdir_x: delentry %s, err=%d\n",
270                                 info.entry.name, ret);
271                 goto clean_up;
272         }
273         /*
274          * If the fillbuf has failed, f_pos is back to 0.
275          * To avoid getting back into the . and .. state
276          * (see comments at the beginning), we put back
277          * the special offset.
278          */
279         filp->f_pos = pos;
280         if (filp->f_pos == 0)
281                 filp->f_pos = start_fpos;
282 out_dput:
283         dput(demd);
284
285 out_end:
286         umsdos_endlookup (dir);
287         
288         Printk ((KERN_DEBUG "read dir %p pos %Ld ret %d\n",
289                 dir, filp->f_pos, ret));
290         return ret;
291 }
292
293
294 /*
295  * Read count directory entries from directory filp.
296  * Return a negative value from linux/errno.h.
297  * Return 0 or positive if successful.
298  */
299  
300 static int UMSDOS_readdir (struct file *filp, void *dirbuf, filldir_t filldir)
301 {
302         struct inode *dir = filp->f_dentry->d_inode;
303         int ret = 0, count = 0;
304         struct UMSDOS_DIR_ONCE bufk;
305
306         lock_kernel();
307
308         bufk.dirbuf = dirbuf;
309         bufk.filldir = filldir;
310         bufk.stop = 0;
311
312         Printk (("UMSDOS_readdir in\n"));
313         while (ret == 0 && bufk.stop == 0) {
314                 struct umsdos_dirent entry;
315
316                 bufk.count = 0;
317                 ret = umsdos_readdir_x (dir, filp, &bufk, &entry, 
318                                         umsdos_dir_once);
319                 if (bufk.count == 0)
320                         break;
321                 count += bufk.count;
322         }
323         unlock_kernel();
324         Printk (("UMSDOS_readdir out %d count %d pos %Ld\n", 
325                 ret, count, filp->f_pos));
326         return count ? : ret;
327 }
328
329
330 /*
331  * Complete the inode content with info from the EMD file.
332  *
333  * This function modifies the state of a dir inode.  It decides
334  * whether the dir is a UMSDOS or DOS directory.  This is done
335  * deeper in umsdos_patch_inode() called at the end of this function.
336  * 
337  * Because it is does disk access, umsdos_patch_inode() may block.
338  * At the same time, another process may get here to initialise
339  * the same directory inode. There are three cases.
340  * 
341  * 1) The inode is already initialised.  We do nothing.
342  * 2) The inode is not initialised.  We lock access and do it.
343  * 3) Like 2 but another process has locked the inode, so we try
344  * to lock it and check right afterward check whether
345  * initialisation is still needed.
346  * 
347  * 
348  * Thanks to the "mem" option of the kernel command line, it was
349  * possible to consistently reproduce this problem by limiting
350  * my memory to 4 MB and running X.
351  *
352  * Do this only if the inode is freshly read, because we will lose
353  * the current (updated) content.
354  *
355  * A lookup of a mount point directory yield the inode into
356  * the other fs, so we don't care about initialising it. iget()
357  * does this automatically.
358  */
359
360 void umsdos_lookup_patch_new(struct dentry *dentry, struct umsdos_info *info)
361 {
362         struct inode *inode = dentry->d_inode;
363         struct umsdos_dirent *entry = &info->entry;
364
365         /*
366          * This part of the initialization depends only on i_patched.
367          */
368         if (UMSDOS_I(inode)->i_patched)
369                 goto out;
370         UMSDOS_I(inode)->i_patched = 1;
371         if (S_ISREG (entry->mode))
372                 entry->mtime = inode->i_mtime;
373         inode->i_mode = entry->mode;
374         inode->i_rdev = to_kdev_t (entry->rdev);
375         inode->i_atime = entry->atime;
376         inode->i_ctime = entry->ctime;
377         inode->i_mtime = entry->mtime;
378         inode->i_uid = entry->uid;
379         inode->i_gid = entry->gid;
380
381         /* #Specification: umsdos / i_nlink
382          * The nlink field of an inode is maintained by the MSDOS file system
383          * for directory and by UMSDOS for other files.  The logic is that
384          * MSDOS is already figuring out what to do for directories and
385          * does nothing for other files.  For MSDOS, there are no hard links
386          * so all file carry nlink==1.  UMSDOS use some info in the
387          * EMD file to plug the correct value.
388          */
389         if (!S_ISDIR (entry->mode)) {
390                 if (entry->nlink > 0) {
391                         inode->i_nlink = entry->nlink;
392                 } else {
393                         printk (KERN_ERR 
394                                 "UMSDOS:  lookup_patch entry->nlink < 1 ???\n");
395                 }
396         }
397         /*
398          * The mode may have changed, so patch the inode again.
399          */
400         umsdos_patch_dentry_inode(dentry, info->f_pos);
401         umsdos_set_dirinfo_new(dentry, info->f_pos);
402
403 out:
404         return;
405 }
406
407
408 /*
409  * Return != 0 if an entry is the pseudo DOS entry in the pseudo root.
410  */
411
412 int umsdos_is_pseudodos (struct inode *dir, struct dentry *dentry)
413 {
414         /* #Specification: pseudo root / DOS hard coded
415          * The pseudo sub-directory DOS in the pseudo root is hard coded.
416          * The name is DOS. This is done this way to help standardised
417          * the umsdos layout. The idea is that from now on /DOS is
418          * a reserved path and nobody will think of using such a path
419          * for a package.
420          */
421         return dir == pseudo_root
422             && dentry->d_name.len == 3
423             && dentry->d_name.name[0] == 'D'
424             && dentry->d_name.name[1] == 'O'
425             && dentry->d_name.name[2] == 'S';
426 }
427
428
429 /*
430  * Check whether a file exists in the current directory.
431  * Return 0 if OK, negative error code if not (ex: -ENOENT).
432  *
433  * fills dentry->d_inode with found inode, and increments its count.
434  * if not found, return -ENOENT.
435  */
436 /* #Specification: umsdos / lookup
437  * A lookup for a file is done in two steps.  First, we
438  * locate the file in the EMD file.  If not present, we
439  * return an error code (-ENOENT).  If it is there, we
440  * repeat the operation on the msdos file system. If
441  * this fails, it means that the file system is not in
442  * sync with the EMD file.   We silently remove this
443  * entry from the EMD file, and return ENOENT.
444  */
445
446 struct dentry *umsdos_lookup_x (struct inode *dir, struct dentry *dentry, int nopseudo)
447 {                               
448         struct dentry *dret = NULL;
449         struct inode *inode;
450         int ret = -ENOENT;
451         struct umsdos_info info;
452
453 #ifdef UMSDOS_DEBUG_VERBOSE
454 printk("umsdos_lookup_x: looking for %s/%s\n", 
455 dentry->d_parent->d_name.name, dentry->d_name.name);
456 #endif
457
458         umsdos_startlookup (dir);
459         if (umsdos_is_pseudodos (dir, dentry)) {
460                 /* #Specification: pseudo root / lookup(DOS)
461                  * A lookup of DOS in the pseudo root will always succeed
462                  * and return the inode of the real root.
463                  */
464                 Printk ((KERN_DEBUG "umsdos_lookup_x: following /DOS\n"));
465                 inode = saved_root->d_inode;
466                 goto out_add;
467         }
468
469         ret = umsdos_parse (dentry->d_name.name, dentry->d_name.len, &info);
470         if (ret) {
471 printk("umsdos_lookup_x: %s/%s parse failed, ret=%d\n", 
472 dentry->d_parent->d_name.name, dentry->d_name.name, ret);
473                 goto out;
474         }
475
476         ret = umsdos_findentry (dentry->d_parent, &info, 0);
477         if (ret) {
478 if (ret != -ENOENT)
479 printk("umsdos_lookup_x: %s/%s findentry failed, ret=%d\n", 
480 dentry->d_parent->d_name.name, dentry->d_name.name, ret);
481                 goto out;
482         }
483 Printk (("lookup %.*s pos %lu ret %d len %d ", 
484 info.fake.len, info.fake.fname, info.f_pos, ret, info.fake.len));
485
486         /* do a real lookup to get the short name ... */
487         dret = umsdos_covered(dentry->d_parent, info.fake.fname, info.fake.len);
488         ret = PTR_ERR(dret);
489         if (IS_ERR(dret)) {
490 printk("umsdos_lookup_x: %s/%s real lookup failed, ret=%d\n", 
491 dentry->d_parent->d_name.name, dentry->d_name.name, ret);
492                 goto out;
493         }
494         inode = dret->d_inode;
495         if (!inode)
496                 goto out_remove;
497         umsdos_lookup_patch_new(dret, &info);
498 #ifdef UMSDOS_DEBUG_VERBOSE
499 printk("umsdos_lookup_x: found %s/%s, ino=%ld\n", 
500 dret->d_parent->d_name.name, dret->d_name.name, dret->d_inode->i_ino);
501 #endif
502
503         /* Check for a hard link */
504         if ((info.entry.flags & UMSDOS_HLINK) &&
505             !UMSDOS_I(inode)->i_is_hlink) {
506                 dret = umsdos_solve_hlink (dret);
507                 ret = PTR_ERR(dret);
508                 if (IS_ERR(dret))
509                         goto out;
510                 ret = -ENOENT;
511                 inode = dret->d_inode;
512                 if (!inode) {
513 printk("umsdos_lookup_x: %s/%s negative after link\n", 
514 dret->d_parent->d_name.name, dret->d_name.name);
515                         goto out_dput;
516                 }
517         }
518
519         if (inode == pseudo_root && !nopseudo) {
520                 /* #Specification: pseudo root / dir lookup
521                  * For the same reason as readdir, a lookup in /DOS for
522                  * the pseudo root directory (linux) will fail.
523                  */
524                 /*
525                  * This has to be allowed for resolving hard links
526                  * which are recorded independently of the pseudo-root
527                  * mode.
528                  */
529 printk("umsdos_lookup_x: skipping DOS/linux\n");
530                 ret = -ENOENT;
531                 goto out_dput;
532         }
533
534         /*
535          * We've found it OK.  Now hash the dentry with the inode.
536          */
537 out_add:
538         atomic_inc(&inode->i_count);
539         d_add (dentry, inode);
540         dentry->d_op = &umsdos_dentry_operations;
541         ret = 0;
542
543 out_dput:
544         if (dret && dret != dentry)
545                 d_drop(dret);
546         dput(dret);
547 out:
548         umsdos_endlookup (dir);
549         return ERR_PTR(ret);
550
551 out_remove:
552         printk(KERN_WARNING "UMSDOS:  entry %s/%s out of sync, erased\n",
553                 dentry->d_parent->d_name.name, dentry->d_name.name);
554         umsdos_delentry (dentry->d_parent, &info, S_ISDIR (info.entry.mode));
555         ret = -ENOENT;
556         goto out_dput;
557 }
558
559
560 /*
561  * Check whether a file exists in the current directory.
562  * Return 0 if OK, negative error code if not (ex: -ENOENT).
563  * 
564  * Called by VFS; should fill dentry->d_inode via d_add.
565  */
566
567 struct dentry *UMSDOS_lookup (struct inode *dir, struct dentry *dentry, struct nameidata *nd)
568 {
569         struct dentry *ret;
570
571         ret = umsdos_lookup_x (dir, dentry, 0);
572
573         /* Create negative dentry if not found. */
574         if (ret == ERR_PTR(-ENOENT)) {
575                 Printk ((KERN_DEBUG 
576                         "UMSDOS_lookup: converting -ENOENT to negative\n"));
577                 d_add (dentry, NULL);
578                 dentry->d_op = &umsdos_dentry_operations;
579                 ret = NULL;
580         }
581         return ret;
582 }
583
584 struct dentry *umsdos_covered(struct dentry *parent, char *name, int len)
585 {
586         struct dentry *result, *dentry;
587         struct qstr qstr;
588
589         qstr.name = name;
590         qstr.len  = len;
591         qstr.hash = full_name_hash(name, len);
592         result = ERR_PTR(-ENOMEM);
593         dentry = d_alloc(parent, &qstr);
594         if (dentry) {
595                 /* XXXXXXXXXXXXXXXXXXX Race alert! */
596                 result = UMSDOS_rlookup(parent->d_inode, dentry);
597                 d_drop(dentry);
598                 if (result)
599                         goto out_fail;
600                 return dentry;
601         }
602 out:
603         return result;
604
605 out_fail:
606         dput(dentry);
607         goto out;
608 }
609
610 /*
611  * Lookup or create a dentry from within the filesystem.
612  *
613  * We need to use this instead of lookup_dentry, as the 
614  * directory semaphore lock is already held.
615  */
616 struct dentry *umsdos_lookup_dentry(struct dentry *parent, char *name, int len,
617                                         int real)
618 {
619         struct dentry *result, *dentry;
620         struct qstr qstr;
621
622         qstr.name = name;
623         qstr.len  = len;
624         qstr.hash = full_name_hash(name, len);
625         result = d_lookup(parent, &qstr);
626         if (!result) {
627                 result = ERR_PTR(-ENOMEM);
628                 dentry = d_alloc(parent, &qstr);
629                 if (dentry) {
630                         result = real ?
631                                 UMSDOS_rlookup(parent->d_inode, dentry) :
632                                 UMSDOS_lookup(parent->d_inode, dentry);
633                         if (result)
634                                 goto out_fail;
635                         return dentry;
636                 }
637         }
638 out:
639         return result;
640
641 out_fail:
642         dput(dentry);
643         goto out;
644 }
645
646 /*
647  * Return a path relative to our root.
648  */
649 char * umsdos_d_path(struct dentry *dentry, char * buffer, int len)
650 {
651         struct dentry * old_root;
652         char * path;
653
654         read_lock(&current->fs->lock);
655         old_root = dget(current->fs->root);
656         read_unlock(&current->fs->lock);
657         spin_lock(&dcache_lock);
658         path = __d_path(dentry, current->fs->rootmnt, dentry->d_sb->s_root, current->fs->rootmnt, buffer, len); /* FIXME: current->fs->rootmnt */
659         spin_unlock(&dcache_lock);
660
661         if (*path == '/')
662                 path++; /* skip leading '/' */
663
664         if (current->fs->root->d_inode == pseudo_root)
665         {
666                 *(path-1) = '/';
667                 path -= (UMSDOS_PSDROOT_LEN+1);
668                 memcpy(path, UMSDOS_PSDROOT_NAME, UMSDOS_PSDROOT_LEN);
669         }
670         dput(old_root);
671
672         return path;
673 }
674
675 /*
676  * Return the dentry which points to a pseudo-hardlink.
677  *
678  * it should try to find file it points to
679  * if file is found, return new dentry/inode
680  * The resolved inode will have i_is_hlink set.
681  *
682  * Note: the original dentry is always dput(), even if an error occurs.
683  */
684
685 struct dentry *umsdos_solve_hlink (struct dentry *hlink)
686 {
687         /* root is our root for resolving pseudo-hardlink */
688         struct dentry *base = hlink->d_sb->s_root;
689         struct dentry *dentry_dst;
690         char *path, *pt;
691         int len;
692         struct address_space *mapping = hlink->d_inode->i_mapping;
693         struct page *page;
694
695         page=read_cache_page(mapping,0,(filler_t *)mapping->a_ops->readpage,NULL);
696         dentry_dst=(struct dentry *)page;
697         if (IS_ERR(page))
698                 goto out;
699         wait_on_page_locked(page);
700         if (!PageUptodate(page))
701                 goto async_fail;
702
703         dentry_dst = ERR_PTR(-ENOMEM);
704         path = (char *) kmalloc (PATH_MAX, GFP_KERNEL);
705         if (path == NULL)
706                 goto out_release;
707         memcpy(path, kmap(page), hlink->d_inode->i_size);
708         kunmap(page);
709         page_cache_release(page);
710
711         len = hlink->d_inode->i_size;
712
713         /* start at root dentry */
714         dentry_dst = dget(base);
715         path[len] = '\0';
716         
717         pt = path;
718         if (*path == '/')
719                 pt++; /* skip leading '/' */
720         
721         if (base->d_inode == pseudo_root)
722                 pt += (UMSDOS_PSDROOT_LEN + 1);
723         
724         while (1) {
725                 struct dentry *dir = dentry_dst, *demd;
726                 char *start = pt;
727                 int real;
728
729                 while (*pt != '\0' && *pt != '/') pt++;
730                 len = (int) (pt - start);
731                 if (*pt == '/') *pt++ = '\0';
732
733                 real = 1;
734                 demd = umsdos_get_emd_dentry(dir);
735                 if (!IS_ERR(demd)) {
736                         if (demd->d_inode)
737                                 real = 0;
738                         dput(demd);
739                 }
740
741 #ifdef UMSDOS_DEBUG_VERBOSE
742 printk ("umsdos_solve_hlink: dir %s/%s, name=%s, real=%d\n",
743 dir->d_parent->d_name.name, dir->d_name.name, start, real);
744 #endif
745                 dentry_dst = umsdos_lookup_dentry(dir, start, len, real);
746 /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
747                 if (real)
748                         d_drop(dir);
749                 dput (dir);
750                 if (IS_ERR(dentry_dst))
751                         break;
752                 /* not found? stop search ... */
753                 if (!dentry_dst->d_inode) {
754                         break;
755                 }
756                 if (*pt == '\0')        /* we're finished! */
757                         break;
758         } /* end while */
759
760         if (!IS_ERR(dentry_dst)) {
761                 struct inode *inode = dentry_dst->d_inode;
762                 if (inode) {
763                         UMSDOS_I(inode)->i_is_hlink = 1;
764 #ifdef UMSDOS_DEBUG_VERBOSE
765 printk ("umsdos_solve_hlink: resolved link %s/%s, ino=%ld\n",
766 dentry_dst->d_parent->d_name.name, dentry_dst->d_name.name, inode->i_ino);
767 #endif
768                 } else {
769 #ifdef UMSDOS_DEBUG_VERBOSE
770 printk ("umsdos_solve_hlink: resolved link %s/%s negative!\n",
771 dentry_dst->d_parent->d_name.name, dentry_dst->d_name.name);
772 #endif
773                 }
774         } else
775                 printk(KERN_WARNING
776                         "umsdos_solve_hlink: err=%ld\n", PTR_ERR(dentry_dst));
777         kfree (path);
778
779 out:
780         dput(hlink);    /* original hlink no longer needed */
781         return dentry_dst;
782
783 async_fail:
784         dentry_dst = ERR_PTR(-EIO);
785 out_release:
786         page_cache_release(page);
787         goto out;
788 }       
789
790
791 struct file_operations umsdos_dir_operations =
792 {
793         .read           = generic_read_dir,
794         .readdir        = UMSDOS_readdir,
795         .ioctl          = UMSDOS_ioctl_dir,
796 };
797
798 struct inode_operations umsdos_dir_inode_operations =
799 {
800         .create         = UMSDOS_create,
801         .lookup         = UMSDOS_lookup,
802         .link           = UMSDOS_link,
803         .unlink         = UMSDOS_unlink,
804         .symlink        = UMSDOS_symlink,
805         .mkdir          = UMSDOS_mkdir,
806         .rmdir          = UMSDOS_rmdir,
807         .mknod          = UMSDOS_mknod,
808         .rename         = UMSDOS_rename,
809         .setattr        = UMSDOS_notify_change,
810 };