at v5.4 6.1 kB view raw
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 */ 14static DEFINE_RWLOCK(adfs_dir_lock); 15 16void 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 54static int 55adfs_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 94unlock_out: 95 read_unlock(&adfs_dir_lock); 96 97free_out: 98 ops->free(&dir); 99 return ret; 100} 101 102int 103adfs_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); 133out: 134#endif 135 return ret; 136} 137 138static unsigned char adfs_tolower(unsigned char c) 139{ 140 if (c >= 'A' && c <= 'Z') 141 c += 'a' - 'A'; 142 return c; 143} 144 145static 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 160static 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 200unlock_out: 201 read_unlock(&adfs_dir_lock); 202 203free_out: 204 ops->free(&dir); 205out: 206 return ret; 207} 208 209const 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 216static int 217adfs_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 */ 240static 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 246const struct dentry_operations adfs_dentry_operations = { 247 .d_hash = adfs_hash, 248 .d_compare = adfs_compare, 249}; 250 251static struct dentry * 252adfs_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 */ 276const struct inode_operations adfs_dir_inode_operations = { 277 .lookup = adfs_lookup, 278 .setattr = adfs_notify_change, 279};