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