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