1 /* 2 * linux/fs/adfs/dir.c 3 * 4 * Copyright (C) 1999-2000 Russell King 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 * 10 * Common directory handling for ADFS 11 */ 12 #include "adfs.h" 13 14 /* 15 * For future. This should probably be per-directory. 16 */ 17 static DEFINE_RWLOCK(adfs_dir_lock); 18 19 void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj) 20 { 21 unsigned int dots, i; 22 23 /* 24 * RISC OS allows the use of '/' in directory entry names, so we need 25 * to fix these up. '/' is typically used for FAT compatibility to 26 * represent '.', so do the same conversion here. In any case, '.' 27 * will never be in a RISC OS name since it is used as the pathname 28 * separator. Handle the case where we may generate a '.' or '..' 29 * name, replacing the first character with '^' (the RISC OS "parent 30 * directory" character.) 31 */ 32 for (i = dots = 0; i < obj->name_len; i++) 33 if (obj->name[i] == '/') { 34 obj->name[i] = '.'; 35 dots++; 36 } 37 38 if (obj->name_len <= 2 && dots == obj->name_len) 39 obj->name[0] = '^'; 40 41 obj->filetype = -1; 42 43 /* 44 * object is a file and is filetyped and timestamped? 45 * RISC OS 12-bit filetype is stored in load_address[19:8] 46 */ 47 if ((0 == (obj->attr & ADFS_NDA_DIRECTORY)) && 48 (0xfff00000 == (0xfff00000 & obj->loadaddr))) { 49 obj->filetype = (__u16) ((0x000fff00 & obj->loadaddr) >> 8); 50 51 /* optionally append the ,xyz hex filetype suffix */ 52 if (ADFS_SB(dir->sb)->s_ftsuffix) { 53 __u16 filetype = obj->filetype; 54 55 obj->name[obj->name_len++] = ','; 56 obj->name[obj->name_len++] = hex_asc_lo(filetype >> 8); 57 obj->name[obj->name_len++] = hex_asc_lo(filetype >> 4); 58 obj->name[obj->name_len++] = hex_asc_lo(filetype >> 0); 59 } 60 } 61 } 62 63 static int 64 adfs_readdir(struct file *file, struct dir_context *ctx) 65 { 66 struct inode *inode = file_inode(file); 67 struct super_block *sb = inode->i_sb; 68 const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; 69 struct object_info obj; 70 struct adfs_dir dir; 71 int ret = 0; 72 73 if (ctx->pos >> 32) 74 return 0; 75 76 ret = ops->read(sb, inode->i_ino, inode->i_size, &dir); 77 if (ret) 78 return ret; 79 80 if (ctx->pos == 0) { 81 if (!dir_emit_dot(file, ctx)) 82 goto free_out; 83 ctx->pos = 1; 84 } 85 if (ctx->pos == 1) { 86 if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR)) 87 goto free_out; 88 ctx->pos = 2; 89 } 90 91 read_lock(&adfs_dir_lock); 92 93 ret = ops->setpos(&dir, ctx->pos - 2); 94 if (ret) 95 goto unlock_out; 96 while (ops->getnext(&dir, &obj) == 0) { 97 if (!dir_emit(ctx, obj.name, obj.name_len, 98 obj.file_id, DT_UNKNOWN)) 99 break; 100 ctx->pos++; 101 } 102 103 unlock_out: 104 read_unlock(&adfs_dir_lock); 105 106 free_out: 107 ops->free(&dir); 108 return ret; 109 } 110 111 int 112 adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait) 113 { 114 int ret = -EINVAL; 115 #ifdef CONFIG_ADFS_FS_RW 116 const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; 117 struct adfs_dir dir; 118 119 printk(KERN_INFO "adfs_dir_update: object %06X in dir %06X\n", 120 obj->file_id, obj->parent_id); 121 122 if (!ops->update) { 123 ret = -EINVAL; 124 goto out; 125 } 126 127 ret = ops->read(sb, obj->parent_id, 0, &dir); 128 if (ret) 129 goto out; 130 131 write_lock(&adfs_dir_lock); 132 ret = ops->update(&dir, obj); 133 write_unlock(&adfs_dir_lock); 134 135 if (wait) { 136 int err = ops->sync(&dir); 137 if (!ret) 138 ret = err; 139 } 140 141 ops->free(&dir); 142 out: 143 #endif 144 return ret; 145 } 146 147 static unsigned char adfs_tolower(unsigned char c) 148 { 149 if (c >= 'A' && c <= 'Z') 150 c += 'a' - 'A'; 151 return c; 152 } 153 154 static int __adfs_compare(const unsigned char *qstr, u32 qlen, 155 const char *str, u32 len) 156 { 157 u32 i; 158 159 if (qlen != len) 160 return 1; 161 162 for (i = 0; i < qlen; i++) 163 if (adfs_tolower(qstr[i]) != adfs_tolower(str[i])) 164 return 1; 165 166 return 0; 167 } 168 169 static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr, 170 struct object_info *obj) 171 { 172 struct super_block *sb = inode->i_sb; 173 const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; 174 const unsigned char *name; 175 struct adfs_dir dir; 176 u32 name_len; 177 int ret; 178 179 ret = ops->read(sb, inode->i_ino, inode->i_size, &dir); 180 if (ret) 181 goto out; 182 183 if (ADFS_I(inode)->parent_id != dir.parent_id) { 184 adfs_error(sb, "parent directory changed under me! (%lx but got %x)\n", 185 ADFS_I(inode)->parent_id, dir.parent_id); 186 ret = -EIO; 187 goto free_out; 188 } 189 190 obj->parent_id = inode->i_ino; 191 192 read_lock(&adfs_dir_lock); 193 194 ret = ops->setpos(&dir, 0); 195 if (ret) 196 goto unlock_out; 197 198 ret = -ENOENT; 199 name = qstr->name; 200 name_len = qstr->len; 201 while (ops->getnext(&dir, obj) == 0) { 202 if (!__adfs_compare(name, name_len, obj->name, obj->name_len)) { 203 ret = 0; 204 break; 205 } 206 } 207 208 unlock_out: 209 read_unlock(&adfs_dir_lock); 210 211 free_out: 212 ops->free(&dir); 213 out: 214 return ret; 215 } 216 217 const struct file_operations adfs_dir_operations = { 218 .read = generic_read_dir, 219 .llseek = generic_file_llseek, 220 .iterate = adfs_readdir, 221 .fsync = generic_file_fsync, 222 }; 223 224 static int 225 adfs_hash(const struct dentry *parent, struct qstr *qstr) 226 { 227 const unsigned char *name; 228 unsigned long hash; 229 u32 len; 230 231 if (qstr->len > ADFS_SB(parent->d_sb)->s_namelen) 232 return -ENAMETOOLONG; 233 234 len = qstr->len; 235 name = qstr->name; 236 hash = init_name_hash(parent); 237 while (len--) 238 hash = partial_name_hash(adfs_tolower(*name++), hash); 239 qstr->hash = end_name_hash(hash); 240 241 return 0; 242 } 243 244 /* 245 * Compare two names, taking note of the name length 246 * requirements of the underlying filesystem. 247 */ 248 static int adfs_compare(const struct dentry *dentry, unsigned int len, 249 const char *str, const struct qstr *qstr) 250 { 251 return __adfs_compare(qstr->name, qstr->len, str, len); 252 } 253 254 const struct dentry_operations adfs_dentry_operations = { 255 .d_hash = adfs_hash, 256 .d_compare = adfs_compare, 257 }; 258 259 static struct dentry * 260 adfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) 261 { 262 struct inode *inode = NULL; 263 struct object_info obj; 264 int error; 265 266 error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj); 267 if (error == 0) { 268 /* 269 * This only returns NULL if get_empty_inode 270 * fails. 271 */ 272 inode = adfs_iget(dir->i_sb, &obj); 273 if (!inode) 274 inode = ERR_PTR(-EACCES); 275 } else if (error != -ENOENT) { 276 inode = ERR_PTR(error); 277 } 278 return d_splice_alias(inode, dentry); 279 } 280 281 /* 282 * directories can handle most operations... 283 */ 284 const struct inode_operations adfs_dir_inode_operations = { 285 .lookup = adfs_lookup, 286 .setattr = adfs_notify_change, 287 }; 288