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