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

Cache object: 214b6ae110e74690ab3b93bec05c48b9


[ 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.