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