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