The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/fs/umsdos/dir.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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 ... */
   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/... */
   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 
   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  
   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  
  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 
  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 
  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 
  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 
  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 
  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  */
  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  */
  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 
  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 };

Cache object: 16927e4e266e00a943389b4b75023ff6


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.