root/fs/adfs/dir.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. adfs_object_fixup
  2. adfs_readdir
  3. adfs_dir_update
  4. adfs_tolower
  5. __adfs_compare
  6. adfs_dir_lookup_byname
  7. adfs_hash
  8. adfs_compare
  9. adfs_lookup

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  *  linux/fs/adfs/dir.c
   4  *
   5  *  Copyright (C) 1999-2000 Russell King
   6  *
   7  *  Common directory handling for ADFS
   8  */
   9 #include "adfs.h"
  10 
  11 /*
  12  * For future.  This should probably be per-directory.
  13  */
  14 static DEFINE_RWLOCK(adfs_dir_lock);
  15 
  16 void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj)
  17 {
  18         unsigned int dots, i;
  19 
  20         /*
  21          * RISC OS allows the use of '/' in directory entry names, so we need
  22          * to fix these up.  '/' is typically used for FAT compatibility to
  23          * represent '.', so do the same conversion here.  In any case, '.'
  24          * will never be in a RISC OS name since it is used as the pathname
  25          * separator.  Handle the case where we may generate a '.' or '..'
  26          * name, replacing the first character with '^' (the RISC OS "parent
  27          * directory" character.)
  28          */
  29         for (i = dots = 0; i < obj->name_len; i++)
  30                 if (obj->name[i] == '/') {
  31                         obj->name[i] = '.';
  32                         dots++;
  33                 }
  34 
  35         if (obj->name_len <= 2 && dots == obj->name_len)
  36                 obj->name[0] = '^';
  37 
  38         /*
  39          * If the object is a file, and the user requested the ,xyz hex
  40          * filetype suffix to the name, check the filetype and append.
  41          */
  42         if (!(obj->attr & ADFS_NDA_DIRECTORY) && ADFS_SB(dir->sb)->s_ftsuffix) {
  43                 u16 filetype = adfs_filetype(obj->loadaddr);
  44 
  45                 if (filetype != ADFS_FILETYPE_NONE) {
  46                         obj->name[obj->name_len++] = ',';
  47                         obj->name[obj->name_len++] = hex_asc_lo(filetype >> 8);
  48                         obj->name[obj->name_len++] = hex_asc_lo(filetype >> 4);
  49                         obj->name[obj->name_len++] = hex_asc_lo(filetype >> 0);
  50                 }
  51         }
  52 }
  53 
  54 static int
  55 adfs_readdir(struct file *file, struct dir_context *ctx)
  56 {
  57         struct inode *inode = file_inode(file);
  58         struct super_block *sb = inode->i_sb;
  59         const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
  60         struct object_info obj;
  61         struct adfs_dir dir;
  62         int ret = 0;
  63 
  64         if (ctx->pos >> 32)
  65                 return 0;
  66 
  67         ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
  68         if (ret)
  69                 return ret;
  70 
  71         if (ctx->pos == 0) {
  72                 if (!dir_emit_dot(file, ctx))
  73                         goto free_out;
  74                 ctx->pos = 1;
  75         }
  76         if (ctx->pos == 1) {
  77                 if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR))
  78                         goto free_out;
  79                 ctx->pos = 2;
  80         }
  81 
  82         read_lock(&adfs_dir_lock);
  83 
  84         ret = ops->setpos(&dir, ctx->pos - 2);
  85         if (ret)
  86                 goto unlock_out;
  87         while (ops->getnext(&dir, &obj) == 0) {
  88                 if (!dir_emit(ctx, obj.name, obj.name_len,
  89                               obj.indaddr, DT_UNKNOWN))
  90                         break;
  91                 ctx->pos++;
  92         }
  93 
  94 unlock_out:
  95         read_unlock(&adfs_dir_lock);
  96 
  97 free_out:
  98         ops->free(&dir);
  99         return ret;
 100 }
 101 
 102 int
 103 adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
 104 {
 105         int ret = -EINVAL;
 106 #ifdef CONFIG_ADFS_FS_RW
 107         const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
 108         struct adfs_dir dir;
 109 
 110         printk(KERN_INFO "adfs_dir_update: object %06x in dir %06x\n",
 111                  obj->indaddr, obj->parent_id);
 112 
 113         if (!ops->update) {
 114                 ret = -EINVAL;
 115                 goto out;
 116         }
 117 
 118         ret = ops->read(sb, obj->parent_id, 0, &dir);
 119         if (ret)
 120                 goto out;
 121 
 122         write_lock(&adfs_dir_lock);
 123         ret = ops->update(&dir, obj);
 124         write_unlock(&adfs_dir_lock);
 125 
 126         if (wait) {
 127                 int err = ops->sync(&dir);
 128                 if (!ret)
 129                         ret = err;
 130         }
 131 
 132         ops->free(&dir);
 133 out:
 134 #endif
 135         return ret;
 136 }
 137 
 138 static unsigned char adfs_tolower(unsigned char c)
 139 {
 140         if (c >= 'A' && c <= 'Z')
 141                 c += 'a' - 'A';
 142         return c;
 143 }
 144 
 145 static int __adfs_compare(const unsigned char *qstr, u32 qlen,
 146                           const char *str, u32 len)
 147 {
 148         u32 i;
 149 
 150         if (qlen != len)
 151                 return 1;
 152 
 153         for (i = 0; i < qlen; i++)
 154                 if (adfs_tolower(qstr[i]) != adfs_tolower(str[i]))
 155                         return 1;
 156 
 157         return 0;
 158 }
 159 
 160 static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,
 161                                   struct object_info *obj)
 162 {
 163         struct super_block *sb = inode->i_sb;
 164         const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
 165         const unsigned char *name;
 166         struct adfs_dir dir;
 167         u32 name_len;
 168         int ret;
 169 
 170         ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
 171         if (ret)
 172                 goto out;
 173 
 174         if (ADFS_I(inode)->parent_id != dir.parent_id) {
 175                 adfs_error(sb,
 176                            "parent directory changed under me! (%06x but got %06x)\n",
 177                            ADFS_I(inode)->parent_id, dir.parent_id);
 178                 ret = -EIO;
 179                 goto free_out;
 180         }
 181 
 182         obj->parent_id = inode->i_ino;
 183 
 184         read_lock(&adfs_dir_lock);
 185 
 186         ret = ops->setpos(&dir, 0);
 187         if (ret)
 188                 goto unlock_out;
 189 
 190         ret = -ENOENT;
 191         name = qstr->name;
 192         name_len = qstr->len;
 193         while (ops->getnext(&dir, obj) == 0) {
 194                 if (!__adfs_compare(name, name_len, obj->name, obj->name_len)) {
 195                         ret = 0;
 196                         break;
 197                 }
 198         }
 199 
 200 unlock_out:
 201         read_unlock(&adfs_dir_lock);
 202 
 203 free_out:
 204         ops->free(&dir);
 205 out:
 206         return ret;
 207 }
 208 
 209 const struct file_operations adfs_dir_operations = {
 210         .read           = generic_read_dir,
 211         .llseek         = generic_file_llseek,
 212         .iterate        = adfs_readdir,
 213         .fsync          = generic_file_fsync,
 214 };
 215 
 216 static int
 217 adfs_hash(const struct dentry *parent, struct qstr *qstr)
 218 {
 219         const unsigned char *name;
 220         unsigned long hash;
 221         u32 len;
 222 
 223         if (qstr->len > ADFS_SB(parent->d_sb)->s_namelen)
 224                 return -ENAMETOOLONG;
 225 
 226         len = qstr->len;
 227         name = qstr->name;
 228         hash = init_name_hash(parent);
 229         while (len--)
 230                 hash = partial_name_hash(adfs_tolower(*name++), hash);
 231         qstr->hash = end_name_hash(hash);
 232 
 233         return 0;
 234 }
 235 
 236 /*
 237  * Compare two names, taking note of the name length
 238  * requirements of the underlying filesystem.
 239  */
 240 static int adfs_compare(const struct dentry *dentry, unsigned int len,
 241                         const char *str, const struct qstr *qstr)
 242 {
 243         return __adfs_compare(qstr->name, qstr->len, str, len);
 244 }
 245 
 246 const struct dentry_operations adfs_dentry_operations = {
 247         .d_hash         = adfs_hash,
 248         .d_compare      = adfs_compare,
 249 };
 250 
 251 static struct dentry *
 252 adfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
 253 {
 254         struct inode *inode = NULL;
 255         struct object_info obj;
 256         int error;
 257 
 258         error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
 259         if (error == 0) {
 260                 /*
 261                  * This only returns NULL if get_empty_inode
 262                  * fails.
 263                  */
 264                 inode = adfs_iget(dir->i_sb, &obj);
 265                 if (!inode)
 266                         inode = ERR_PTR(-EACCES);
 267         } else if (error != -ENOENT) {
 268                 inode = ERR_PTR(error);
 269         }
 270         return d_splice_alias(inode, dentry);
 271 }
 272 
 273 /*
 274  * directories can handle most operations...
 275  */
 276 const struct inode_operations adfs_dir_inode_operations = {
 277         .lookup         = adfs_lookup,
 278         .setattr        = adfs_notify_change,
 279 };

/* [<][>][^][v][top][bottom][index][help] */