Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  *  linux/fs/adfs/dir.c
0004  *
0005  *  Copyright (C) 1999-2000 Russell King
0006  *
0007  *  Common directory handling for ADFS
0008  */
0009 #include <linux/slab.h>
0010 #include "adfs.h"
0011 
0012 /*
0013  * For future.  This should probably be per-directory.
0014  */
0015 static DECLARE_RWSEM(adfs_dir_rwsem);
0016 
0017 int adfs_dir_copyfrom(void *dst, struct adfs_dir *dir, unsigned int offset,
0018               size_t len)
0019 {
0020     struct super_block *sb = dir->sb;
0021     unsigned int index, remain;
0022 
0023     index = offset >> sb->s_blocksize_bits;
0024     offset &= sb->s_blocksize - 1;
0025     remain = sb->s_blocksize - offset;
0026     if (index + (remain < len) >= dir->nr_buffers)
0027         return -EINVAL;
0028 
0029     if (remain < len) {
0030         memcpy(dst, dir->bhs[index]->b_data + offset, remain);
0031         dst += remain;
0032         len -= remain;
0033         index += 1;
0034         offset = 0;
0035     }
0036 
0037     memcpy(dst, dir->bhs[index]->b_data + offset, len);
0038 
0039     return 0;
0040 }
0041 
0042 int adfs_dir_copyto(struct adfs_dir *dir, unsigned int offset, const void *src,
0043             size_t len)
0044 {
0045     struct super_block *sb = dir->sb;
0046     unsigned int index, remain;
0047 
0048     index = offset >> sb->s_blocksize_bits;
0049     offset &= sb->s_blocksize - 1;
0050     remain = sb->s_blocksize - offset;
0051     if (index + (remain < len) >= dir->nr_buffers)
0052         return -EINVAL;
0053 
0054     if (remain < len) {
0055         memcpy(dir->bhs[index]->b_data + offset, src, remain);
0056         src += remain;
0057         len -= remain;
0058         index += 1;
0059         offset = 0;
0060     }
0061 
0062     memcpy(dir->bhs[index]->b_data + offset, src, len);
0063 
0064     return 0;
0065 }
0066 
0067 static void __adfs_dir_cleanup(struct adfs_dir *dir)
0068 {
0069     dir->nr_buffers = 0;
0070 
0071     if (dir->bhs != dir->bh)
0072         kfree(dir->bhs);
0073     dir->bhs = NULL;
0074     dir->sb = NULL;
0075 }
0076 
0077 void adfs_dir_relse(struct adfs_dir *dir)
0078 {
0079     unsigned int i;
0080 
0081     for (i = 0; i < dir->nr_buffers; i++)
0082         brelse(dir->bhs[i]);
0083 
0084     __adfs_dir_cleanup(dir);
0085 }
0086 
0087 static void adfs_dir_forget(struct adfs_dir *dir)
0088 {
0089     unsigned int i;
0090 
0091     for (i = 0; i < dir->nr_buffers; i++)
0092         bforget(dir->bhs[i]);
0093 
0094     __adfs_dir_cleanup(dir);
0095 }
0096 
0097 int adfs_dir_read_buffers(struct super_block *sb, u32 indaddr,
0098               unsigned int size, struct adfs_dir *dir)
0099 {
0100     struct buffer_head **bhs;
0101     unsigned int i, num;
0102     int block;
0103 
0104     num = ALIGN(size, sb->s_blocksize) >> sb->s_blocksize_bits;
0105     if (num > ARRAY_SIZE(dir->bh)) {
0106         /* We only allow one extension */
0107         if (dir->bhs != dir->bh)
0108             return -EINVAL;
0109 
0110         bhs = kcalloc(num, sizeof(*bhs), GFP_KERNEL);
0111         if (!bhs)
0112             return -ENOMEM;
0113 
0114         if (dir->nr_buffers)
0115             memcpy(bhs, dir->bhs, dir->nr_buffers * sizeof(*bhs));
0116 
0117         dir->bhs = bhs;
0118     }
0119 
0120     for (i = dir->nr_buffers; i < num; i++) {
0121         block = __adfs_block_map(sb, indaddr, i);
0122         if (!block) {
0123             adfs_error(sb, "dir %06x has a hole at offset %u",
0124                    indaddr, i);
0125             goto error;
0126         }
0127 
0128         dir->bhs[i] = sb_bread(sb, block);
0129         if (!dir->bhs[i]) {
0130             adfs_error(sb,
0131                    "dir %06x failed read at offset %u, mapped block 0x%08x",
0132                    indaddr, i, block);
0133             goto error;
0134         }
0135 
0136         dir->nr_buffers++;
0137     }
0138     return 0;
0139 
0140 error:
0141     adfs_dir_relse(dir);
0142 
0143     return -EIO;
0144 }
0145 
0146 static int adfs_dir_read(struct super_block *sb, u32 indaddr,
0147              unsigned int size, struct adfs_dir *dir)
0148 {
0149     dir->sb = sb;
0150     dir->bhs = dir->bh;
0151     dir->nr_buffers = 0;
0152 
0153     return ADFS_SB(sb)->s_dir->read(sb, indaddr, size, dir);
0154 }
0155 
0156 static int adfs_dir_read_inode(struct super_block *sb, struct inode *inode,
0157                    struct adfs_dir *dir)
0158 {
0159     int ret;
0160 
0161     ret = adfs_dir_read(sb, ADFS_I(inode)->indaddr, inode->i_size, dir);
0162     if (ret)
0163         return ret;
0164 
0165     if (ADFS_I(inode)->parent_id != dir->parent_id) {
0166         adfs_error(sb,
0167                "parent directory id changed under me! (%06x but got %06x)\n",
0168                ADFS_I(inode)->parent_id, dir->parent_id);
0169         adfs_dir_relse(dir);
0170         ret = -EIO;
0171     }
0172 
0173     return ret;
0174 }
0175 
0176 static void adfs_dir_mark_dirty(struct adfs_dir *dir)
0177 {
0178     unsigned int i;
0179 
0180     /* Mark the buffers dirty */
0181     for (i = 0; i < dir->nr_buffers; i++)
0182         mark_buffer_dirty(dir->bhs[i]);
0183 }
0184 
0185 static int adfs_dir_sync(struct adfs_dir *dir)
0186 {
0187     int err = 0;
0188     int i;
0189 
0190     for (i = dir->nr_buffers - 1; i >= 0; i--) {
0191         struct buffer_head *bh = dir->bhs[i];
0192         sync_dirty_buffer(bh);
0193         if (buffer_req(bh) && !buffer_uptodate(bh))
0194             err = -EIO;
0195     }
0196 
0197     return err;
0198 }
0199 
0200 void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj)
0201 {
0202     unsigned int dots, i;
0203 
0204     /*
0205      * RISC OS allows the use of '/' in directory entry names, so we need
0206      * to fix these up.  '/' is typically used for FAT compatibility to
0207      * represent '.', so do the same conversion here.  In any case, '.'
0208      * will never be in a RISC OS name since it is used as the pathname
0209      * separator.  Handle the case where we may generate a '.' or '..'
0210      * name, replacing the first character with '^' (the RISC OS "parent
0211      * directory" character.)
0212      */
0213     for (i = dots = 0; i < obj->name_len; i++)
0214         if (obj->name[i] == '/') {
0215             obj->name[i] = '.';
0216             dots++;
0217         }
0218 
0219     if (obj->name_len <= 2 && dots == obj->name_len)
0220         obj->name[0] = '^';
0221 
0222     /*
0223      * If the object is a file, and the user requested the ,xyz hex
0224      * filetype suffix to the name, check the filetype and append.
0225      */
0226     if (!(obj->attr & ADFS_NDA_DIRECTORY) && ADFS_SB(dir->sb)->s_ftsuffix) {
0227         u16 filetype = adfs_filetype(obj->loadaddr);
0228 
0229         if (filetype != ADFS_FILETYPE_NONE) {
0230             obj->name[obj->name_len++] = ',';
0231             obj->name[obj->name_len++] = hex_asc_lo(filetype >> 8);
0232             obj->name[obj->name_len++] = hex_asc_lo(filetype >> 4);
0233             obj->name[obj->name_len++] = hex_asc_lo(filetype >> 0);
0234         }
0235     }
0236 }
0237 
0238 static int adfs_iterate(struct file *file, struct dir_context *ctx)
0239 {
0240     struct inode *inode = file_inode(file);
0241     struct super_block *sb = inode->i_sb;
0242     const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
0243     struct adfs_dir dir;
0244     int ret;
0245 
0246     down_read(&adfs_dir_rwsem);
0247     ret = adfs_dir_read_inode(sb, inode, &dir);
0248     if (ret)
0249         goto unlock;
0250 
0251     if (ctx->pos == 0) {
0252         if (!dir_emit_dot(file, ctx))
0253             goto unlock_relse;
0254         ctx->pos = 1;
0255     }
0256     if (ctx->pos == 1) {
0257         if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR))
0258             goto unlock_relse;
0259         ctx->pos = 2;
0260     }
0261 
0262     ret = ops->iterate(&dir, ctx);
0263 
0264 unlock_relse:
0265     up_read(&adfs_dir_rwsem);
0266     adfs_dir_relse(&dir);
0267     return ret;
0268 
0269 unlock:
0270     up_read(&adfs_dir_rwsem);
0271     return ret;
0272 }
0273 
0274 int
0275 adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
0276 {
0277     const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
0278     struct adfs_dir dir;
0279     int ret;
0280 
0281     if (!IS_ENABLED(CONFIG_ADFS_FS_RW))
0282         return -EINVAL;
0283 
0284     if (!ops->update)
0285         return -EINVAL;
0286 
0287     down_write(&adfs_dir_rwsem);
0288     ret = adfs_dir_read(sb, obj->parent_id, 0, &dir);
0289     if (ret)
0290         goto unlock;
0291 
0292     ret = ops->update(&dir, obj);
0293     if (ret)
0294         goto forget;
0295 
0296     ret = ops->commit(&dir);
0297     if (ret)
0298         goto forget;
0299     up_write(&adfs_dir_rwsem);
0300 
0301     adfs_dir_mark_dirty(&dir);
0302 
0303     if (wait)
0304         ret = adfs_dir_sync(&dir);
0305 
0306     adfs_dir_relse(&dir);
0307     return ret;
0308 
0309     /*
0310      * If the updated failed because the entry wasn't found, we can
0311      * just release the buffers. If it was any other error, forget
0312      * the dirtied buffers so they aren't written back to the media.
0313      */
0314 forget:
0315     if (ret == -ENOENT)
0316         adfs_dir_relse(&dir);
0317     else
0318         adfs_dir_forget(&dir);
0319 unlock:
0320     up_write(&adfs_dir_rwsem);
0321 
0322     return ret;
0323 }
0324 
0325 static unsigned char adfs_tolower(unsigned char c)
0326 {
0327     if (c >= 'A' && c <= 'Z')
0328         c += 'a' - 'A';
0329     return c;
0330 }
0331 
0332 static int __adfs_compare(const unsigned char *qstr, u32 qlen,
0333               const char *str, u32 len)
0334 {
0335     u32 i;
0336 
0337     if (qlen != len)
0338         return 1;
0339 
0340     for (i = 0; i < qlen; i++)
0341         if (adfs_tolower(qstr[i]) != adfs_tolower(str[i]))
0342             return 1;
0343 
0344     return 0;
0345 }
0346 
0347 static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,
0348                   struct object_info *obj)
0349 {
0350     struct super_block *sb = inode->i_sb;
0351     const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
0352     const unsigned char *name;
0353     struct adfs_dir dir;
0354     u32 name_len;
0355     int ret;
0356 
0357     down_read(&adfs_dir_rwsem);
0358     ret = adfs_dir_read_inode(sb, inode, &dir);
0359     if (ret)
0360         goto unlock;
0361 
0362     ret = ops->setpos(&dir, 0);
0363     if (ret)
0364         goto unlock_relse;
0365 
0366     ret = -ENOENT;
0367     name = qstr->name;
0368     name_len = qstr->len;
0369     while (ops->getnext(&dir, obj) == 0) {
0370         if (!__adfs_compare(name, name_len, obj->name, obj->name_len)) {
0371             ret = 0;
0372             break;
0373         }
0374     }
0375     obj->parent_id = ADFS_I(inode)->indaddr;
0376 
0377 unlock_relse:
0378     up_read(&adfs_dir_rwsem);
0379     adfs_dir_relse(&dir);
0380     return ret;
0381 
0382 unlock:
0383     up_read(&adfs_dir_rwsem);
0384     return ret;
0385 }
0386 
0387 const struct file_operations adfs_dir_operations = {
0388     .read       = generic_read_dir,
0389     .llseek     = generic_file_llseek,
0390     .iterate_shared = adfs_iterate,
0391     .fsync      = generic_file_fsync,
0392 };
0393 
0394 static int
0395 adfs_hash(const struct dentry *parent, struct qstr *qstr)
0396 {
0397     const unsigned char *name;
0398     unsigned long hash;
0399     u32 len;
0400 
0401     if (qstr->len > ADFS_SB(parent->d_sb)->s_namelen)
0402         return -ENAMETOOLONG;
0403 
0404     len = qstr->len;
0405     name = qstr->name;
0406     hash = init_name_hash(parent);
0407     while (len--)
0408         hash = partial_name_hash(adfs_tolower(*name++), hash);
0409     qstr->hash = end_name_hash(hash);
0410 
0411     return 0;
0412 }
0413 
0414 /*
0415  * Compare two names, taking note of the name length
0416  * requirements of the underlying filesystem.
0417  */
0418 static int adfs_compare(const struct dentry *dentry, unsigned int len,
0419             const char *str, const struct qstr *qstr)
0420 {
0421     return __adfs_compare(qstr->name, qstr->len, str, len);
0422 }
0423 
0424 const struct dentry_operations adfs_dentry_operations = {
0425     .d_hash     = adfs_hash,
0426     .d_compare  = adfs_compare,
0427 };
0428 
0429 static struct dentry *
0430 adfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
0431 {
0432     struct inode *inode = NULL;
0433     struct object_info obj;
0434     int error;
0435 
0436     error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
0437     if (error == 0) {
0438         /*
0439          * This only returns NULL if get_empty_inode
0440          * fails.
0441          */
0442         inode = adfs_iget(dir->i_sb, &obj);
0443         if (!inode)
0444             inode = ERR_PTR(-EACCES);
0445     } else if (error != -ENOENT) {
0446         inode = ERR_PTR(error);
0447     }
0448     return d_splice_alias(inode, dentry);
0449 }
0450 
0451 /*
0452  * directories can handle most operations...
0453  */
0454 const struct inode_operations adfs_dir_inode_operations = {
0455     .lookup     = adfs_lookup,
0456     .setattr    = adfs_notify_change,
0457 };