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) 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 ops->free(&dir); 110 out: 111 #endif 112 return ret; 113 } 114 115 static int 116 adfs_match(struct qstr *name, struct object_info *obj) 117 { 118 int i; 119 120 if (name->len != obj->name_len) 121 return 0; 122 123 for (i = 0; i < name->len; i++) { 124 char c1, c2; 125 126 c1 = name->name[i]; 127 c2 = obj->name[i]; 128 129 if (c1 >= 'A' && c1 <= 'Z') 130 c1 += 'a' - 'A'; 131 if (c2 >= 'A' && c2 <= 'Z') 132 c2 += 'a' - 'A'; 133 134 if (c1 != c2) 135 return 0; 136 } 137 return 1; 138 } 139 140 static int 141 adfs_dir_lookup_byname(struct inode *inode, struct qstr *name, struct object_info *obj) 142 { 143 struct super_block *sb = inode->i_sb; 144 struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; 145 struct adfs_dir dir; 146 int ret; 147 148 ret = ops->read(sb, inode->i_ino, inode->i_size, &dir); 149 if (ret) 150 goto out; 151 152 if (ADFS_I(inode)->parent_id != dir.parent_id) { 153 adfs_error(sb, "parent directory changed under me! (%lx but got %lx)\n", 154 ADFS_I(inode)->parent_id, dir.parent_id); 155 ret = -EIO; 156 goto free_out; 157 } 158 159 obj->parent_id = inode->i_ino; 160 161 /* 162 * '.' is handled by reserved_lookup() in fs/namei.c 163 */ 164 if (name->len == 2 && name->name[0] == '.' && name->name[1] == '.') { 165 /* 166 * Currently unable to fill in the rest of 'obj', 167 * but this is better than nothing. We need to 168 * ascend one level to find it's parent. 169 */ 170 obj->name_len = 0; 171 obj->file_id = obj->parent_id; 172 goto free_out; 173 } 174 175 read_lock(&adfs_dir_lock); 176 177 ret = ops->setpos(&dir, 0); 178 if (ret) 179 goto unlock_out; 180 181 ret = -ENOENT; 182 while (ops->getnext(&dir, obj) == 0) { 183 if (adfs_match(name, obj)) { 184 ret = 0; 185 break; 186 } 187 } 188 189 unlock_out: 190 read_unlock(&adfs_dir_lock); 191 192 free_out: 193 ops->free(&dir); 194 out: 195 return ret; 196 } 197 198 const struct file_operations adfs_dir_operations = { 199 .read = generic_read_dir, 200 .llseek = generic_file_llseek, 201 .readdir = adfs_readdir, 202 .fsync = file_fsync, 203 }; 204 205 static int 206 adfs_hash(struct dentry *parent, struct qstr *qstr) 207 { 208 const unsigned int name_len = ADFS_SB(parent->d_sb)->s_namelen; 209 const unsigned char *name; 210 unsigned long hash; 211 int i; 212 213 if (qstr->len < name_len) 214 return 0; 215 216 /* 217 * Truncate the name in place, avoids 218 * having to define a compare function. 219 */ 220 qstr->len = i = name_len; 221 name = qstr->name; 222 hash = init_name_hash(); 223 while (i--) { 224 char c; 225 226 c = *name++; 227 if (c >= 'A' && c <= 'Z') 228 c += 'a' - 'A'; 229 230 hash = partial_name_hash(c, hash); 231 } 232 qstr->hash = end_name_hash(hash); 233 234 return 0; 235 } 236 237 /* 238 * Compare two names, taking note of the name length 239 * requirements of the underlying filesystem. 240 */ 241 static int 242 adfs_compare(struct dentry *parent, struct qstr *entry, struct qstr *name) 243 { 244 int i; 245 246 if (entry->len != name->len) 247 return 1; 248 249 for (i = 0; i < name->len; i++) { 250 char a, b; 251 252 a = entry->name[i]; 253 b = name->name[i]; 254 255 if (a >= 'A' && a <= 'Z') 256 a += 'a' - 'A'; 257 if (b >= 'A' && b <= 'Z') 258 b += 'a' - 'A'; 259 260 if (a != b) 261 return 1; 262 } 263 return 0; 264 } 265 266 struct dentry_operations adfs_dentry_operations = { 267 .d_hash = adfs_hash, 268 .d_compare = adfs_compare, 269 }; 270 271 static struct dentry * 272 adfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) 273 { 274 struct inode *inode = NULL; 275 struct object_info obj; 276 int error; 277 278 dentry->d_op = &adfs_dentry_operations; 279 lock_kernel(); 280 error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj); 281 if (error == 0) { 282 error = -EACCES; 283 /* 284 * This only returns NULL if get_empty_inode 285 * fails. 286 */ 287 inode = adfs_iget(dir->i_sb, &obj); 288 if (inode) 289 error = 0; 290 } 291 unlock_kernel(); 292 d_add(dentry, inode); 293 return ERR_PTR(error); 294 } 295 296 /* 297 * directories can handle most operations... 298 */ 299 const struct inode_operations adfs_dir_inode_operations = { 300 .lookup = adfs_lookup, 301 .setattr = adfs_notify_change, 302 }; 303