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 obj->filetype = -1; 39 40 /* 41 * object is a file and is filetyped and timestamped? 42 * RISC OS 12-bit filetype is stored in load_address[19:8] 43 */ 44 if ((0 == (obj->attr & ADFS_NDA_DIRECTORY)) && 45 (0xfff00000 == (0xfff00000 & obj->loadaddr))) { 46 obj->filetype = (__u16) ((0x000fff00 & obj->loadaddr) >> 8); 47 48 /* optionally append the ,xyz hex filetype suffix */ 49 if (ADFS_SB(dir->sb)->s_ftsuffix) { 50 __u16 filetype = obj->filetype; 51 52 obj->name[obj->name_len++] = ','; 53 obj->name[obj->name_len++] = hex_asc_lo(filetype >> 8); 54 obj->name[obj->name_len++] = hex_asc_lo(filetype >> 4); 55 obj->name[obj->name_len++] = hex_asc_lo(filetype >> 0); 56 } 57 } 58 } 59 60 static int 61 adfs_readdir(struct file *file, struct dir_context *ctx) 62 { 63 struct inode *inode = file_inode(file); 64 struct super_block *sb = inode->i_sb; 65 const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; 66 struct object_info obj; 67 struct adfs_dir dir; 68 int ret = 0; 69 70 if (ctx->pos >> 32) 71 return 0; 72 73 ret = ops->read(sb, inode->i_ino, inode->i_size, &dir); 74 if (ret) 75 return ret; 76 77 if (ctx->pos == 0) { 78 if (!dir_emit_dot(file, ctx)) 79 goto free_out; 80 ctx->pos = 1; 81 } 82 if (ctx->pos == 1) { 83 if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR)) 84 goto free_out; 85 ctx->pos = 2; 86 } 87 88 read_lock(&adfs_dir_lock); 89 90 ret = ops->setpos(&dir, ctx->pos - 2); 91 if (ret) 92 goto unlock_out; 93 while (ops->getnext(&dir, &obj) == 0) { 94 if (!dir_emit(ctx, obj.name, obj.name_len, 95 obj.file_id, DT_UNKNOWN)) 96 break; 97 ctx->pos++; 98 } 99 100 unlock_out: 101 read_unlock(&adfs_dir_lock); 102 103 free_out: 104 ops->free(&dir); 105 return ret; 106 } 107 108 int 109 adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait) 110 { 111 int ret = -EINVAL; 112 #ifdef CONFIG_ADFS_FS_RW 113 const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; 114 struct adfs_dir dir; 115 116 printk(KERN_INFO "adfs_dir_update: object %06X in dir %06X\n", 117 obj->file_id, obj->parent_id); 118 119 if (!ops->update) { 120 ret = -EINVAL; 121 goto out; 122 } 123 124 ret = ops->read(sb, obj->parent_id, 0, &dir); 125 if (ret) 126 goto out; 127 128 write_lock(&adfs_dir_lock); 129 ret = ops->update(&dir, obj); 130 write_unlock(&adfs_dir_lock); 131 132 if (wait) { 133 int err = ops->sync(&dir); 134 if (!ret) 135 ret = err; 136 } 137 138 ops->free(&dir); 139 out: 140 #endif 141 return ret; 142 } 143 144 static unsigned char adfs_tolower(unsigned char c) 145 { 146 if (c >= 'A' && c <= 'Z') 147 c += 'a' - 'A'; 148 return c; 149 } 150 151 static int __adfs_compare(const unsigned char *qstr, u32 qlen, 152 const char *str, u32 len) 153 { 154 u32 i; 155 156 if (qlen != len) 157 return 1; 158 159 for (i = 0; i < qlen; i++) 160 if (adfs_tolower(qstr[i]) != adfs_tolower(str[i])) 161 return 1; 162 163 return 0; 164 } 165 166 static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr, 167 struct object_info *obj) 168 { 169 struct super_block *sb = inode->i_sb; 170 const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; 171 const unsigned char *name; 172 struct adfs_dir dir; 173 u32 name_len; 174 int ret; 175 176 ret = ops->read(sb, inode->i_ino, inode->i_size, &dir); 177 if (ret) 178 goto out; 179 180 if (ADFS_I(inode)->parent_id != dir.parent_id) { 181 adfs_error(sb, "parent directory changed under me! (%lx but got %x)\n", 182 ADFS_I(inode)->parent_id, dir.parent_id); 183 ret = -EIO; 184 goto free_out; 185 } 186 187 obj->parent_id = inode->i_ino; 188 189 read_lock(&adfs_dir_lock); 190 191 ret = ops->setpos(&dir, 0); 192 if (ret) 193 goto unlock_out; 194 195 ret = -ENOENT; 196 name = qstr->name; 197 name_len = qstr->len; 198 while (ops->getnext(&dir, obj) == 0) { 199 if (!__adfs_compare(name, name_len, obj->name, obj->name_len)) { 200 ret = 0; 201 break; 202 } 203 } 204 205 unlock_out: 206 read_unlock(&adfs_dir_lock); 207 208 free_out: 209 ops->free(&dir); 210 out: 211 return ret; 212 } 213 214 const struct file_operations adfs_dir_operations = { 215 .read = generic_read_dir, 216 .llseek = generic_file_llseek, 217 .iterate = adfs_readdir, 218 .fsync = generic_file_fsync, 219 }; 220 221 static int 222 adfs_hash(const struct dentry *parent, struct qstr *qstr) 223 { 224 const unsigned char *name; 225 unsigned long hash; 226 u32 len; 227 228 if (qstr->len > ADFS_SB(parent->d_sb)->s_namelen) 229 return -ENAMETOOLONG; 230 231 len = qstr->len; 232 name = qstr->name; 233 hash = init_name_hash(parent); 234 while (len--) 235 hash = partial_name_hash(adfs_tolower(*name++), hash); 236 qstr->hash = end_name_hash(hash); 237 238 return 0; 239 } 240 241 /* 242 * Compare two names, taking note of the name length 243 * requirements of the underlying filesystem. 244 */ 245 static int adfs_compare(const struct dentry *dentry, unsigned int len, 246 const char *str, const struct qstr *qstr) 247 { 248 return __adfs_compare(qstr->name, qstr->len, str, len); 249 } 250 251 const struct dentry_operations adfs_dentry_operations = { 252 .d_hash = adfs_hash, 253 .d_compare = adfs_compare, 254 }; 255 256 static struct dentry * 257 adfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) 258 { 259 struct inode *inode = NULL; 260 struct object_info obj; 261 int error; 262 263 error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj); 264 if (error == 0) { 265 /* 266 * This only returns NULL if get_empty_inode 267 * fails. 268 */ 269 inode = adfs_iget(dir->i_sb, &obj); 270 if (!inode) 271 inode = ERR_PTR(-EACCES); 272 } else if (error != -ENOENT) { 273 inode = ERR_PTR(error); 274 } 275 return d_splice_alias(inode, dentry); 276 } 277 278 /* 279 * directories can handle most operations... 280 */ 281 const struct inode_operations adfs_dir_inode_operations = { 282 .lookup = adfs_lookup, 283 .setattr = adfs_notify_change, 284 }; 285