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