1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * linux/fs/adfs/dir_fplus.c 4 * 5 * Copyright (C) 1997-1999 Russell King 6 */ 7 #include "adfs.h" 8 #include "dir_fplus.h" 9 10 static int adfs_fplus_read(struct super_block *sb, u32 indaddr, 11 unsigned int size, struct adfs_dir *dir) 12 { 13 struct adfs_bigdirheader *h; 14 struct adfs_bigdirtail *t; 15 unsigned int dirsize; 16 int ret; 17 18 /* Read first buffer */ 19 ret = adfs_dir_read_buffers(sb, indaddr, sb->s_blocksize, dir); 20 if (ret) 21 return ret; 22 23 h = (struct adfs_bigdirheader *)dir->bhs[0]->b_data; 24 dirsize = le32_to_cpu(h->bigdirsize); 25 if (dirsize != size) { 26 adfs_msg(sb, KERN_WARNING, 27 "dir %06x header size %X does not match directory size %X", 28 indaddr, dirsize, size); 29 } 30 31 if (h->bigdirversion[0] != 0 || h->bigdirversion[1] != 0 || 32 h->bigdirversion[2] != 0 || size & 2047 || 33 h->bigdirstartname != cpu_to_le32(BIGDIRSTARTNAME)) { 34 adfs_error(sb, "dir %06x has malformed header", indaddr); 35 goto out; 36 } 37 38 /* Read remaining buffers */ 39 ret = adfs_dir_read_buffers(sb, indaddr, dirsize, dir); 40 if (ret) 41 return ret; 42 43 t = (struct adfs_bigdirtail *) 44 (dir->bhs[dir->nr_buffers - 1]->b_data + (sb->s_blocksize - 8)); 45 46 if (t->bigdirendname != cpu_to_le32(BIGDIRENDNAME) || 47 t->bigdirendmasseq != h->startmasseq || 48 t->reserved[0] != 0 || t->reserved[1] != 0) { 49 adfs_error(sb, "dir %06x has malformed tail", indaddr); 50 goto out; 51 } 52 53 dir->parent_id = le32_to_cpu(h->bigdirparent); 54 return 0; 55 56 out: 57 adfs_dir_relse(dir); 58 59 return ret; 60 } 61 62 static int 63 adfs_fplus_setpos(struct adfs_dir *dir, unsigned int fpos) 64 { 65 struct adfs_bigdirheader *h = 66 (struct adfs_bigdirheader *) dir->bhs[0]->b_data; 67 int ret = -ENOENT; 68 69 if (fpos <= le32_to_cpu(h->bigdirentries)) { 70 dir->pos = fpos; 71 ret = 0; 72 } 73 74 return ret; 75 } 76 77 static int 78 adfs_fplus_getnext(struct adfs_dir *dir, struct object_info *obj) 79 { 80 struct adfs_bigdirheader *h = 81 (struct adfs_bigdirheader *) dir->bhs[0]->b_data; 82 struct adfs_bigdirentry bde; 83 unsigned int offset; 84 int ret; 85 86 if (dir->pos >= le32_to_cpu(h->bigdirentries)) 87 return -ENOENT; 88 89 offset = offsetof(struct adfs_bigdirheader, bigdirname); 90 offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3); 91 offset += dir->pos * sizeof(struct adfs_bigdirentry); 92 93 ret = adfs_dir_copyfrom(&bde, dir, offset, 94 sizeof(struct adfs_bigdirentry)); 95 if (ret) 96 return ret; 97 98 obj->loadaddr = le32_to_cpu(bde.bigdirload); 99 obj->execaddr = le32_to_cpu(bde.bigdirexec); 100 obj->size = le32_to_cpu(bde.bigdirlen); 101 obj->indaddr = le32_to_cpu(bde.bigdirindaddr); 102 obj->attr = le32_to_cpu(bde.bigdirattr); 103 obj->name_len = le32_to_cpu(bde.bigdirobnamelen); 104 105 offset = offsetof(struct adfs_bigdirheader, bigdirname); 106 offset += ((le32_to_cpu(h->bigdirnamelen) + 4) & ~3); 107 offset += le32_to_cpu(h->bigdirentries) * sizeof(struct adfs_bigdirentry); 108 offset += le32_to_cpu(bde.bigdirobnameptr); 109 110 ret = adfs_dir_copyfrom(obj->name, dir, offset, obj->name_len); 111 if (ret) 112 return ret; 113 114 adfs_object_fixup(dir, obj); 115 116 dir->pos += 1; 117 118 return 0; 119 } 120 121 const struct adfs_dir_ops adfs_fplus_dir_ops = { 122 .read = adfs_fplus_read, 123 .setpos = adfs_fplus_setpos, 124 .getnext = adfs_fplus_getnext, 125 }; 126