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/config.h> 13 #include <linux/errno.h> 14 #include <linux/fs.h> 15 #include <linux/adfs_fs.h> 16 #include <linux/time.h> 17 #include <linux/stat.h> 18 #include <linux/spinlock.h> 19 #include <linux/smp_lock.h> 20 #include <linux/buffer_head.h> /* for file_fsync() */ 21 22 #include "adfs.h" 23 24 /* 25 * For future. This should probably be per-directory. 26 */ 27 static DEFINE_RWLOCK(adfs_dir_lock); 28 29 static int 30 adfs_readdir(struct file *filp, void *dirent, filldir_t filldir) 31 { 32 struct inode *inode = filp->f_dentry->d_inode; 33 struct super_block *sb = inode->i_sb; 34 struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; 35 struct object_info obj; 36 struct adfs_dir dir; 37 int ret = 0; 38 39 lock_kernel(); 40 41 if (filp->f_pos >> 32) 42 goto out; 43 44 ret = ops->read(sb, inode->i_ino, inode->i_size, &dir); 45 if (ret) 46 goto out; 47 48 switch ((unsigned long)filp->f_pos) { 49 case 0: 50 if (filldir(dirent, ".", 1, 0, inode->i_ino, DT_DIR) < 0) 51 goto free_out; 52 filp->f_pos += 1; 53 54 case 1: 55 if (filldir(dirent, "..", 2, 1, dir.parent_id, DT_DIR) < 0) 56 goto free_out; 57 filp->f_pos += 1; 58 59 default: 60 break; 61 } 62 63 read_lock(&adfs_dir_lock); 64 65 ret = ops->setpos(&dir, filp->f_pos - 2); 66 if (ret) 67 goto unlock_out; 68 while (ops->getnext(&dir, &obj) == 0) { 69 if (filldir(dirent, obj.name, obj.name_len, 70 filp->f_pos, obj.file_id, DT_UNKNOWN) < 0) 71 goto unlock_out; 72 filp->f_pos += 1; 73 } 74 75 unlock_out: 76 read_unlock(&adfs_dir_lock); 77 78 free_out: 79 ops->free(&dir); 80 81 out: 82 unlock_kernel(); 83 return ret; 84 } 85 86 int 87 adfs_dir_update(struct super_block *sb, struct object_info *obj) 88 { 89 int ret = -EINVAL; 90 #ifdef CONFIG_ADFS_FS_RW 91 struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; 92 struct adfs_dir dir; 93 94 printk(KERN_INFO "adfs_dir_update: object %06X in dir %06X\n", 95 obj->file_id, obj->parent_id); 96 97 if (!ops->update) { 98 ret = -EINVAL; 99 goto out; 100 } 101 102 ret = ops->read(sb, obj->parent_id, 0, &dir); 103 if (ret) 104 goto out; 105 106 write_lock(&adfs_dir_lock); 107 ret = ops->update(&dir, obj); 108 write_unlock(&adfs_dir_lock); 109 110 ops->free(&dir); 111 out: 112 #endif 113 return ret; 114 } 115 116 static int 117 adfs_match(struct qstr *name, struct object_info *obj) 118 { 119 int i; 120 121 if (name->len != obj->name_len) 122 return 0; 123 124 for (i = 0; i < name->len; i++) { 125 char c1, c2; 126 127 c1 = name->name[i]; 128 c2 = obj->name[i]; 129 130 if (c1 >= 'A' && c1 <= 'Z') 131 c1 += 'a' - 'A'; 132 if (c2 >= 'A' && c2 <= 'Z') 133 c2 += 'a' - 'A'; 134 135 if (c1 != c2) 136 return 0; 137 } 138 return 1; 139 } 140 141 static int 142 adfs_dir_lookup_byname(struct inode *inode, struct qstr *name, struct object_info *obj) 143 { 144 struct super_block *sb = inode->i_sb; 145 struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; 146 struct adfs_dir dir; 147 int ret; 148 149 ret = ops->read(sb, inode->i_ino, inode->i_size, &dir); 150 if (ret) 151 goto out; 152 153 if (ADFS_I(inode)->parent_id != dir.parent_id) { 154 adfs_error(sb, "parent directory changed under me! (%lx but got %lx)\n", 155 ADFS_I(inode)->parent_id, dir.parent_id); 156 ret = -EIO; 157 goto free_out; 158 } 159 160 obj->parent_id = inode->i_ino; 161 162 /* 163 * '.' is handled by reserved_lookup() in fs/namei.c 164 */ 165 if (name->len == 2 && name->name[0] == '.' && name->name[1] == '.') { 166 /* 167 * Currently unable to fill in the rest of 'obj', 168 * but this is better than nothing. We need to 169 * ascend one level to find it's parent. 170 */ 171 obj->name_len = 0; 172 obj->file_id = obj->parent_id; 173 goto free_out; 174 } 175 176 read_lock(&adfs_dir_lock); 177 178 ret = ops->setpos(&dir, 0); 179 if (ret) 180 goto unlock_out; 181 182 ret = -ENOENT; 183 while (ops->getnext(&dir, obj) == 0) { 184 if (adfs_match(name, obj)) { 185 ret = 0; 186 break; 187 } 188 } 189 190 unlock_out: 191 read_unlock(&adfs_dir_lock); 192 193 free_out: 194 ops->free(&dir); 195 out: 196 return ret; 197 } 198 199 struct file_operations adfs_dir_operations = { 200 .read = generic_read_dir, 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 struct inode_operations adfs_dir_inode_operations = { 300 .lookup = adfs_lookup, 301 .setattr = adfs_notify_change, 302 }; 303