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 <linux/slab.h> 10 #include "adfs.h" 11 12 /* 13 * For future. This should probably be per-directory. 14 */ 15 static DEFINE_RWLOCK(adfs_dir_lock); 16 17 int adfs_dir_copyfrom(void *dst, struct adfs_dir *dir, unsigned int offset, 18 size_t len) 19 { 20 struct super_block *sb = dir->sb; 21 unsigned int index, remain; 22 23 index = offset >> sb->s_blocksize_bits; 24 offset &= sb->s_blocksize - 1; 25 remain = sb->s_blocksize - offset; 26 if (index + (remain < len) >= dir->nr_buffers) 27 return -EINVAL; 28 29 if (remain < len) { 30 memcpy(dst, dir->bhs[index]->b_data + offset, remain); 31 dst += remain; 32 len -= remain; 33 index += 1; 34 offset = 0; 35 } 36 37 memcpy(dst, dir->bhs[index]->b_data + offset, len); 38 39 return 0; 40 } 41 42 int adfs_dir_copyto(struct adfs_dir *dir, unsigned int offset, const void *src, 43 size_t len) 44 { 45 struct super_block *sb = dir->sb; 46 unsigned int index, remain; 47 48 index = offset >> sb->s_blocksize_bits; 49 offset &= sb->s_blocksize - 1; 50 remain = sb->s_blocksize - offset; 51 if (index + (remain < len) >= dir->nr_buffers) 52 return -EINVAL; 53 54 if (remain < len) { 55 memcpy(dir->bhs[index]->b_data + offset, src, remain); 56 src += remain; 57 len -= remain; 58 index += 1; 59 offset = 0; 60 } 61 62 memcpy(dir->bhs[index]->b_data + offset, src, len); 63 64 return 0; 65 } 66 67 void adfs_dir_relse(struct adfs_dir *dir) 68 { 69 unsigned int i; 70 71 for (i = 0; i < dir->nr_buffers; i++) 72 brelse(dir->bhs[i]); 73 dir->nr_buffers = 0; 74 75 if (dir->bhs != dir->bh) 76 kfree(dir->bhs); 77 dir->bhs = NULL; 78 dir->sb = NULL; 79 } 80 81 int adfs_dir_read_buffers(struct super_block *sb, u32 indaddr, 82 unsigned int size, struct adfs_dir *dir) 83 { 84 struct buffer_head **bhs; 85 unsigned int i, num; 86 int block; 87 88 num = ALIGN(size, sb->s_blocksize) >> sb->s_blocksize_bits; 89 if (num > ARRAY_SIZE(dir->bh)) { 90 /* We only allow one extension */ 91 if (dir->bhs != dir->bh) 92 return -EINVAL; 93 94 bhs = kcalloc(num, sizeof(*bhs), GFP_KERNEL); 95 if (!bhs) 96 return -ENOMEM; 97 98 if (dir->nr_buffers) 99 memcpy(bhs, dir->bhs, dir->nr_buffers * sizeof(*bhs)); 100 101 dir->bhs = bhs; 102 } 103 104 for (i = dir->nr_buffers; i < num; i++) { 105 block = __adfs_block_map(sb, indaddr, i); 106 if (!block) { 107 adfs_error(sb, "dir %06x has a hole at offset %u", 108 indaddr, i); 109 goto error; 110 } 111 112 dir->bhs[i] = sb_bread(sb, block); 113 if (!dir->bhs[i]) { 114 adfs_error(sb, 115 "dir %06x failed read at offset %u, mapped block 0x%08x", 116 indaddr, i, block); 117 goto error; 118 } 119 120 dir->nr_buffers++; 121 } 122 return 0; 123 124 error: 125 adfs_dir_relse(dir); 126 127 return -EIO; 128 } 129 130 static int adfs_dir_read(struct super_block *sb, u32 indaddr, 131 unsigned int size, struct adfs_dir *dir) 132 { 133 dir->sb = sb; 134 dir->bhs = dir->bh; 135 dir->nr_buffers = 0; 136 137 return ADFS_SB(sb)->s_dir->read(sb, indaddr, size, dir); 138 } 139 140 static int adfs_dir_read_inode(struct super_block *sb, struct inode *inode, 141 struct adfs_dir *dir) 142 { 143 int ret; 144 145 ret = adfs_dir_read(sb, inode->i_ino, inode->i_size, dir); 146 if (ret) 147 return ret; 148 149 if (ADFS_I(inode)->parent_id != dir->parent_id) { 150 adfs_error(sb, 151 "parent directory id changed under me! (%06x but got %06x)\n", 152 ADFS_I(inode)->parent_id, dir->parent_id); 153 adfs_dir_relse(dir); 154 ret = -EIO; 155 } 156 157 return ret; 158 } 159 160 static void adfs_dir_mark_dirty(struct adfs_dir *dir) 161 { 162 unsigned int i; 163 164 /* Mark the buffers dirty */ 165 for (i = 0; i < dir->nr_buffers; i++) 166 mark_buffer_dirty(dir->bhs[i]); 167 } 168 169 static int adfs_dir_sync(struct adfs_dir *dir) 170 { 171 int err = 0; 172 int i; 173 174 for (i = dir->nr_buffers - 1; i >= 0; i--) { 175 struct buffer_head *bh = dir->bhs[i]; 176 sync_dirty_buffer(bh); 177 if (buffer_req(bh) && !buffer_uptodate(bh)) 178 err = -EIO; 179 } 180 181 return err; 182 } 183 184 void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj) 185 { 186 unsigned int dots, i; 187 188 /* 189 * RISC OS allows the use of '/' in directory entry names, so we need 190 * to fix these up. '/' is typically used for FAT compatibility to 191 * represent '.', so do the same conversion here. In any case, '.' 192 * will never be in a RISC OS name since it is used as the pathname 193 * separator. Handle the case where we may generate a '.' or '..' 194 * name, replacing the first character with '^' (the RISC OS "parent 195 * directory" character.) 196 */ 197 for (i = dots = 0; i < obj->name_len; i++) 198 if (obj->name[i] == '/') { 199 obj->name[i] = '.'; 200 dots++; 201 } 202 203 if (obj->name_len <= 2 && dots == obj->name_len) 204 obj->name[0] = '^'; 205 206 /* 207 * If the object is a file, and the user requested the ,xyz hex 208 * filetype suffix to the name, check the filetype and append. 209 */ 210 if (!(obj->attr & ADFS_NDA_DIRECTORY) && ADFS_SB(dir->sb)->s_ftsuffix) { 211 u16 filetype = adfs_filetype(obj->loadaddr); 212 213 if (filetype != ADFS_FILETYPE_NONE) { 214 obj->name[obj->name_len++] = ','; 215 obj->name[obj->name_len++] = hex_asc_lo(filetype >> 8); 216 obj->name[obj->name_len++] = hex_asc_lo(filetype >> 4); 217 obj->name[obj->name_len++] = hex_asc_lo(filetype >> 0); 218 } 219 } 220 } 221 222 static int 223 adfs_readdir(struct file *file, struct dir_context *ctx) 224 { 225 struct inode *inode = file_inode(file); 226 struct super_block *sb = inode->i_sb; 227 const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; 228 struct object_info obj; 229 struct adfs_dir dir; 230 int ret = 0; 231 232 if (ctx->pos >> 32) 233 return 0; 234 235 ret = adfs_dir_read_inode(sb, inode, &dir); 236 if (ret) 237 return ret; 238 239 if (ctx->pos == 0) { 240 if (!dir_emit_dot(file, ctx)) 241 goto free_out; 242 ctx->pos = 1; 243 } 244 if (ctx->pos == 1) { 245 if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR)) 246 goto free_out; 247 ctx->pos = 2; 248 } 249 250 read_lock(&adfs_dir_lock); 251 252 ret = ops->setpos(&dir, ctx->pos - 2); 253 if (ret) 254 goto unlock_out; 255 while (ops->getnext(&dir, &obj) == 0) { 256 if (!dir_emit(ctx, obj.name, obj.name_len, 257 obj.indaddr, DT_UNKNOWN)) 258 break; 259 ctx->pos++; 260 } 261 262 unlock_out: 263 read_unlock(&adfs_dir_lock); 264 265 free_out: 266 adfs_dir_relse(&dir); 267 return ret; 268 } 269 270 int 271 adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait) 272 { 273 int ret = -EINVAL; 274 #ifdef CONFIG_ADFS_FS_RW 275 const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; 276 struct adfs_dir dir; 277 278 printk(KERN_INFO "adfs_dir_update: object %06x in dir %06x\n", 279 obj->indaddr, obj->parent_id); 280 281 if (!ops->update) 282 return -EINVAL; 283 284 ret = adfs_dir_read(sb, obj->parent_id, 0, &dir); 285 if (ret) 286 goto out; 287 288 write_lock(&adfs_dir_lock); 289 ret = ops->update(&dir, obj); 290 write_unlock(&adfs_dir_lock); 291 292 if (ret == 0) 293 adfs_dir_mark_dirty(&dir); 294 295 if (wait) { 296 int err = adfs_dir_sync(&dir); 297 if (!ret) 298 ret = err; 299 } 300 301 adfs_dir_relse(&dir); 302 out: 303 #endif 304 return ret; 305 } 306 307 static unsigned char adfs_tolower(unsigned char c) 308 { 309 if (c >= 'A' && c <= 'Z') 310 c += 'a' - 'A'; 311 return c; 312 } 313 314 static int __adfs_compare(const unsigned char *qstr, u32 qlen, 315 const char *str, u32 len) 316 { 317 u32 i; 318 319 if (qlen != len) 320 return 1; 321 322 for (i = 0; i < qlen; i++) 323 if (adfs_tolower(qstr[i]) != adfs_tolower(str[i])) 324 return 1; 325 326 return 0; 327 } 328 329 static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr, 330 struct object_info *obj) 331 { 332 struct super_block *sb = inode->i_sb; 333 const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; 334 const unsigned char *name; 335 struct adfs_dir dir; 336 u32 name_len; 337 int ret; 338 339 ret = adfs_dir_read_inode(sb, inode, &dir); 340 if (ret) 341 goto out; 342 343 obj->parent_id = inode->i_ino; 344 345 read_lock(&adfs_dir_lock); 346 347 ret = ops->setpos(&dir, 0); 348 if (ret) 349 goto unlock_out; 350 351 ret = -ENOENT; 352 name = qstr->name; 353 name_len = qstr->len; 354 while (ops->getnext(&dir, obj) == 0) { 355 if (!__adfs_compare(name, name_len, obj->name, obj->name_len)) { 356 ret = 0; 357 break; 358 } 359 } 360 361 unlock_out: 362 read_unlock(&adfs_dir_lock); 363 364 free_out: 365 adfs_dir_relse(&dir); 366 out: 367 return ret; 368 } 369 370 const struct file_operations adfs_dir_operations = { 371 .read = generic_read_dir, 372 .llseek = generic_file_llseek, 373 .iterate = adfs_readdir, 374 .fsync = generic_file_fsync, 375 }; 376 377 static int 378 adfs_hash(const struct dentry *parent, struct qstr *qstr) 379 { 380 const unsigned char *name; 381 unsigned long hash; 382 u32 len; 383 384 if (qstr->len > ADFS_SB(parent->d_sb)->s_namelen) 385 return -ENAMETOOLONG; 386 387 len = qstr->len; 388 name = qstr->name; 389 hash = init_name_hash(parent); 390 while (len--) 391 hash = partial_name_hash(adfs_tolower(*name++), hash); 392 qstr->hash = end_name_hash(hash); 393 394 return 0; 395 } 396 397 /* 398 * Compare two names, taking note of the name length 399 * requirements of the underlying filesystem. 400 */ 401 static int adfs_compare(const struct dentry *dentry, unsigned int len, 402 const char *str, const struct qstr *qstr) 403 { 404 return __adfs_compare(qstr->name, qstr->len, str, len); 405 } 406 407 const struct dentry_operations adfs_dentry_operations = { 408 .d_hash = adfs_hash, 409 .d_compare = adfs_compare, 410 }; 411 412 static struct dentry * 413 adfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) 414 { 415 struct inode *inode = NULL; 416 struct object_info obj; 417 int error; 418 419 error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj); 420 if (error == 0) { 421 /* 422 * This only returns NULL if get_empty_inode 423 * fails. 424 */ 425 inode = adfs_iget(dir->i_sb, &obj); 426 if (!inode) 427 inode = ERR_PTR(-EACCES); 428 } else if (error != -ENOENT) { 429 inode = ERR_PTR(error); 430 } 431 return d_splice_alias(inode, dentry); 432 } 433 434 /* 435 * directories can handle most operations... 436 */ 437 const struct inode_operations adfs_dir_inode_operations = { 438 .lookup = adfs_lookup, 439 .setattr = adfs_notify_change, 440 }; 441