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 }; 280