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/affs/namei.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/affs/namei.c
    3  *
    4  *  (c) 1996  Hans-Joachim Widmaier - Rewritten
    5  *
    6  *  (C) 1993  Ray Burr - Modified for Amiga FFS filesystem.
    7  *
    8  *  (C) 1991  Linus Torvalds - minix filesystem
    9  */
   10 
   11 #include <linux/sched.h>
   12 #include <linux/affs_fs.h>
   13 #include <linux/kernel.h>
   14 #include <linux/string.h>
   15 #include <linux/stat.h>
   16 #include <linux/fcntl.h>
   17 #include <linux/locks.h>
   18 #include <linux/amigaffs.h>
   19 #include <asm/uaccess.h>
   20 
   21 #include <linux/errno.h>
   22 
   23 typedef int (*toupper_t)(int);
   24 
   25 extern struct inode_operations affs_symlink_inode_operations;
   26 
   27 static int       affs_toupper(int ch);
   28 static int       affs_hash_dentry(struct dentry *, struct qstr *);
   29 static int       affs_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
   30 static int       affs_intl_toupper(int ch);
   31 static int       affs_intl_hash_dentry(struct dentry *, struct qstr *);
   32 static int       affs_intl_compare_dentry(struct dentry *, struct qstr *, struct qstr *);
   33 
   34 struct dentry_operations affs_dentry_operations = {
   35         d_hash:         affs_hash_dentry,
   36         d_compare:      affs_compare_dentry,
   37 };
   38 
   39 struct dentry_operations affs_intl_dentry_operations = {
   40         d_hash:         affs_intl_hash_dentry,
   41         d_compare:      affs_intl_compare_dentry,
   42 };
   43 
   44 
   45 /* Simple toupper() for DOS\1 */
   46 
   47 static int
   48 affs_toupper(int ch)
   49 {
   50         return ch >= 'a' && ch <= 'z' ? ch -= ('a' - 'A') : ch;
   51 }
   52 
   53 /* International toupper() for DOS\3 ("international") */
   54 
   55 static int
   56 affs_intl_toupper(int ch)
   57 {
   58         return (ch >= 'a' && ch <= 'z') || (ch >= 0xE0
   59                 && ch <= 0xFE && ch != 0xF7) ?
   60                 ch - ('a' - 'A') : ch;
   61 }
   62 
   63 static inline toupper_t
   64 affs_get_toupper(struct super_block *sb)
   65 {
   66         return AFFS_SB->s_flags & SF_INTL ? affs_intl_toupper : affs_toupper;
   67 }
   68 
   69 /*
   70  * Note: the dentry argument is the parent dentry.
   71  */
   72 static inline int
   73 __affs_hash_dentry(struct dentry *dentry, struct qstr *qstr, toupper_t toupper)
   74 {
   75         const u8 *name = qstr->name;
   76         unsigned long hash;
   77         int i;
   78 
   79         i = affs_check_name(qstr->name,qstr->len);
   80         if (i)
   81                 return i;
   82 
   83         hash = init_name_hash();
   84         i = min(qstr->len, 30u);
   85         for (; i > 0; name++, i--)
   86                 hash = partial_name_hash(toupper(*name), hash);
   87         qstr->hash = end_name_hash(hash);
   88 
   89         return 0;
   90 }
   91 
   92 static int
   93 affs_hash_dentry(struct dentry *dentry, struct qstr *qstr)
   94 {
   95         return __affs_hash_dentry(dentry, qstr, affs_toupper);
   96 }
   97 static int
   98 affs_intl_hash_dentry(struct dentry *dentry, struct qstr *qstr)
   99 {
  100         return __affs_hash_dentry(dentry, qstr, affs_intl_toupper);
  101 }
  102 
  103 static inline int
  104 __affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b, toupper_t toupper)
  105 {
  106         const u8 *aname = a->name;
  107         const u8 *bname = b->name;
  108         int len;
  109 
  110         /* 'a' is the qstr of an already existing dentry, so the name
  111          * must be valid. 'b' must be validated first.
  112          */
  113 
  114         if (affs_check_name(b->name,b->len))
  115                 return 1;
  116 
  117         /* If the names are longer than the allowed 30 chars,
  118          * the excess is ignored, so their length may differ.
  119          */
  120         len = a->len;
  121         if (len >= 30) {
  122                 if (b->len < 30)
  123                         return 1;
  124                 len = 30;
  125         } else if (len != b->len)
  126                 return 1;
  127 
  128         for (; len > 0; len--)
  129                 if (toupper(*aname++) != toupper(*bname++))
  130                         return 1;
  131 
  132         return 0;
  133 }
  134 
  135 static int
  136 affs_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
  137 {
  138         return __affs_compare_dentry(dentry, a, b, affs_toupper);
  139 }
  140 static int
  141 affs_intl_compare_dentry(struct dentry *dentry, struct qstr *a, struct qstr *b)
  142 {
  143         return __affs_compare_dentry(dentry, a, b, affs_intl_toupper);
  144 }
  145 
  146 /*
  147  * NOTE! unlike strncmp, affs_match returns 1 for success, 0 for failure.
  148  */
  149 
  150 static inline int
  151 affs_match(struct dentry *dentry, const u8 *name2, toupper_t toupper)
  152 {
  153         const u8 *name = dentry->d_name.name;
  154         int len = dentry->d_name.len;
  155 
  156         if (len >= 30) {
  157                 if (*name2 < 30)
  158                         return 0;
  159                 len = 30;
  160         } else if (len != *name2)
  161                 return 0;
  162 
  163         for (name2++; len > 0; len--)
  164                 if (toupper(*name++) != toupper(*name2++))
  165                         return 0;
  166         return 1;
  167 }
  168 
  169 int
  170 affs_hash_name(struct super_block *sb, const u8 *name, unsigned int len)
  171 {
  172         toupper_t toupper = affs_get_toupper(sb);
  173         int hash;
  174 
  175         hash = len = min(len, 30u);
  176         for (; len > 0; len--)
  177                 hash = (hash * 13 + toupper(*name++)) & 0x7ff;
  178 
  179         return hash % AFFS_SB->s_hashsize;
  180 }
  181 
  182 static struct buffer_head *
  183 affs_find_entry(struct inode *dir, struct dentry *dentry)
  184 {
  185         struct super_block *sb = dir->i_sb;
  186         struct buffer_head *bh;
  187         toupper_t toupper = affs_get_toupper(sb);
  188         u32 key;
  189 
  190         pr_debug("AFFS: find_entry(\"%.*s\")\n", (int)dentry->d_name.len, dentry->d_name.name);
  191 
  192         bh = affs_bread(sb, dir->i_ino);
  193         if (!bh)
  194                 return ERR_PTR(-EIO);
  195 
  196         key = be32_to_cpu(AFFS_HEAD(bh)->table[affs_hash_name(sb, dentry->d_name.name, dentry->d_name.len)]);
  197 
  198         for (;;) {
  199                 affs_brelse(bh);
  200                 if (key == 0)
  201                         return NULL;
  202                 bh = affs_bread(sb, key);
  203                 if (!bh)
  204                         return ERR_PTR(-EIO);
  205                 if (affs_match(dentry, AFFS_TAIL(sb, bh)->name, toupper))
  206                         return bh;
  207                 key = be32_to_cpu(AFFS_TAIL(sb, bh)->hash_chain);
  208         }
  209 }
  210 
  211 struct dentry *
  212 affs_lookup(struct inode *dir, struct dentry *dentry)
  213 {
  214         struct super_block *sb = dir->i_sb;
  215         struct buffer_head *bh;
  216         struct inode *inode = NULL;
  217 
  218         pr_debug("AFFS: lookup(\"%.*s\")\n",(int)dentry->d_name.len,dentry->d_name.name);
  219 
  220         affs_lock_dir(dir);
  221         bh = affs_find_entry(dir, dentry);
  222         affs_unlock_dir(dir);
  223         if (IS_ERR(bh))
  224                 return ERR_PTR(PTR_ERR(bh));
  225         if (bh) {
  226                 u32 ino = bh->b_blocknr;
  227 
  228                 /* store the real header ino in d_fsdata for faster lookups */
  229                 dentry->d_fsdata = (void *)(long)ino;
  230                 switch (be32_to_cpu(AFFS_TAIL(sb, bh)->stype)) {
  231                 //link to dirs disabled
  232                 //case ST_LINKDIR:
  233                 case ST_LINKFILE:
  234                         ino = be32_to_cpu(AFFS_TAIL(sb, bh)->original);
  235                 }
  236                 affs_brelse(bh);
  237                 inode = iget(sb, ino);
  238                 if (!inode)
  239                         return ERR_PTR(-EACCES);
  240         }
  241         dentry->d_op = AFFS_SB->s_flags & SF_INTL ? &affs_intl_dentry_operations : &affs_dentry_operations;
  242         d_add(dentry, inode);
  243         return NULL;
  244 }
  245 
  246 int
  247 affs_unlink(struct inode *dir, struct dentry *dentry)
  248 {
  249         pr_debug("AFFS: unlink(dir=%d, \"%.*s\")\n", (u32)dir->i_ino,
  250                  (int)dentry->d_name.len, dentry->d_name.name);
  251 
  252         if (!dentry->d_inode)
  253                 return -ENOENT;
  254 
  255         return affs_remove_header(dentry);
  256 }
  257 
  258 int
  259 affs_create(struct inode *dir, struct dentry *dentry, int mode)
  260 {
  261         struct super_block *sb = dir->i_sb;
  262         struct inode    *inode;
  263         int              error;
  264 
  265         pr_debug("AFFS: create(%lu,\"%.*s\",0%o)\n",dir->i_ino,(int)dentry->d_name.len,
  266                  dentry->d_name.name,mode);
  267 
  268         inode = affs_new_inode(dir);
  269         if (!inode)
  270                 return -ENOSPC;
  271 
  272         inode->i_mode = mode;
  273         mode_to_prot(inode);
  274         mark_inode_dirty(inode);
  275 
  276         inode->i_op = &affs_file_inode_operations;
  277         inode->i_fop = &affs_file_operations;
  278         inode->i_mapping->a_ops = (AFFS_SB->s_flags & SF_OFS) ? &affs_aops_ofs : &affs_aops;
  279         error = affs_add_entry(dir, inode, dentry, ST_FILE);
  280         if (error) {
  281                 inode->i_nlink = 0;
  282                 iput(inode);
  283                 return error;
  284         }
  285         return 0;
  286 }
  287 
  288 int
  289 affs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
  290 {
  291         struct inode            *inode;
  292         int                      error;
  293 
  294         pr_debug("AFFS: mkdir(%lu,\"%.*s\",0%o)\n",dir->i_ino,
  295                  (int)dentry->d_name.len,dentry->d_name.name,mode);
  296 
  297         inode = affs_new_inode(dir);
  298         if (!inode)
  299                 return -ENOSPC;
  300 
  301         inode->i_mode = S_IFDIR | mode;
  302         mode_to_prot(inode);
  303 
  304         inode->i_op = &affs_dir_inode_operations;
  305         inode->i_fop = &affs_dir_operations;
  306 
  307         error = affs_add_entry(dir, inode, dentry, ST_USERDIR);
  308         if (error) {
  309                 inode->i_nlink = 0;
  310                 mark_inode_dirty(inode);
  311                 iput(inode);
  312                 return error;
  313         }
  314         return 0;
  315 }
  316 
  317 int
  318 affs_rmdir(struct inode *dir, struct dentry *dentry)
  319 {
  320         pr_debug("AFFS: rmdir(dir=%u, \"%.*s\")\n", (u32)dir->i_ino,
  321                  (int)dentry->d_name.len, dentry->d_name.name);
  322 
  323         if (!dentry->d_inode)
  324                 return -ENOENT;
  325 
  326         return affs_remove_header(dentry);
  327 }
  328 
  329 int
  330 affs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)
  331 {
  332         struct super_block      *sb = dir->i_sb;
  333         struct buffer_head      *bh;
  334         struct inode            *inode;
  335         char                    *p;
  336         int                      i, maxlen, error;
  337         char                     c, lc;
  338 
  339         pr_debug("AFFS: symlink(%lu,\"%.*s\" -> \"%s\")\n",dir->i_ino,
  340                  (int)dentry->d_name.len,dentry->d_name.name,symname);
  341 
  342         maxlen = AFFS_SB->s_hashsize * sizeof(u32) - 1;
  343         error = -ENOSPC;
  344         inode  = affs_new_inode(dir);
  345         if (!inode)
  346                 return -ENOSPC;
  347 
  348         inode->i_op = &affs_symlink_inode_operations;
  349         inode->i_data.a_ops = &affs_symlink_aops;
  350         inode->i_mode = S_IFLNK | 0777;
  351         mode_to_prot(inode);
  352 
  353         error = -EIO;
  354         bh = affs_bread(sb, inode->i_ino);
  355         if (!bh)
  356                 goto err;
  357         i  = 0;
  358         p  = (char *)AFFS_HEAD(bh)->table;
  359         lc = '/';
  360         if (*symname == '/') {
  361                 while (*symname == '/')
  362                         symname++;
  363                 while (AFFS_SB->s_volume[i])    /* Cannot overflow */
  364                         *p++ = AFFS_SB->s_volume[i++];
  365         }
  366         while (i < maxlen && (c = *symname++)) {
  367                 if (c == '.' && lc == '/' && *symname == '.' && symname[1] == '/') {
  368                         *p++ = '/';
  369                         i++;
  370                         symname += 2;
  371                         lc = '/';
  372                 } else if (c == '.' && lc == '/' && *symname == '/') {
  373                         symname++;
  374                         lc = '/';
  375                 } else {
  376                         *p++ = c;
  377                         lc   = c;
  378                         i++;
  379                 }
  380                 if (lc == '/')
  381                         while (*symname == '/')
  382                                 symname++;
  383         }
  384         *p = 0;
  385         mark_buffer_dirty_inode(bh, inode);
  386         affs_brelse(bh);
  387         mark_inode_dirty(inode);
  388 
  389         error = affs_add_entry(dir, inode, dentry, ST_SOFTLINK);
  390         if (error)
  391                 goto err;
  392 
  393         return 0;
  394 
  395 err:
  396         inode->i_nlink = 0;
  397         mark_inode_dirty(inode);
  398         iput(inode);
  399         return error;
  400 }
  401 
  402 int
  403 affs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
  404 {
  405         struct inode *inode = old_dentry->d_inode;
  406         int error;
  407 
  408         pr_debug("AFFS: link(%u, %u, \"%.*s\")\n", (u32)inode->i_ino, (u32)dir->i_ino,
  409                  (int)dentry->d_name.len,dentry->d_name.name);
  410 
  411         if (S_ISDIR(inode->i_mode))
  412                 return -EPERM;
  413 
  414         error = affs_add_entry(dir, inode, dentry, S_ISDIR(inode->i_mode) ? ST_LINKDIR : ST_LINKFILE);
  415         if (error) {
  416                 inode->i_nlink = 0;
  417                 mark_inode_dirty(inode);
  418                 iput(inode);
  419                 return error;
  420         }
  421         return 0;
  422 }
  423 
  424 int
  425 affs_rename(struct inode *old_dir, struct dentry *old_dentry,
  426             struct inode *new_dir, struct dentry *new_dentry)
  427 {
  428         struct super_block *sb = old_dir->i_sb;
  429         struct buffer_head *bh = NULL;
  430         int retval;
  431 
  432         pr_debug("AFFS: rename(old=%u,\"%*s\" to new=%u,\"%*s\")\n",
  433                  (u32)old_dir->i_ino, (int)old_dentry->d_name.len, old_dentry->d_name.name,
  434                  (u32)new_dir->i_ino, (int)new_dentry->d_name.len, new_dentry->d_name.name);
  435 
  436         if ((retval = affs_check_name(new_dentry->d_name.name,new_dentry->d_name.len)))
  437                 goto done;
  438 
  439         /* Unlink destination if it already exists */
  440         if (new_dentry->d_inode) {
  441                 retval = affs_remove_header(new_dentry);
  442                 if (retval)
  443                         return retval;
  444         }
  445 
  446         retval = -EIO;
  447         bh = affs_bread(sb, old_dentry->d_inode->i_ino);
  448         if (!bh)
  449                 goto done;
  450 
  451         /* Remove header from its parent directory. */
  452         affs_lock_dir(old_dir);
  453         retval = affs_remove_hash(old_dir, bh);
  454         affs_unlock_dir(old_dir);
  455         if (retval)
  456                 goto done;
  457 
  458         /* And insert it into the new directory with the new name. */
  459         affs_copy_name(AFFS_TAIL(sb, bh)->name, new_dentry);
  460         affs_fix_checksum(sb, bh);
  461         affs_lock_dir(new_dir);
  462         retval = affs_insert_hash(new_dir, bh);
  463         affs_unlock_dir(new_dir);
  464         /* TODO: move it back to old_dir, if error? */
  465 
  466 done:
  467         mark_buffer_dirty_inode(bh, retval ? old_dir : new_dir);
  468         affs_brelse(bh);
  469         return retval;
  470 }

Cache object: 47e9b474425848b1a6fe116ffc08666b


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