0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/slab.h>
0010 #include "adfs.h"
0011
0012
0013
0014
0015 static DECLARE_RWSEM(adfs_dir_rwsem);
0016
0017 int adfs_dir_copyfrom(void *dst, struct adfs_dir *dir, unsigned int offset,
0018 size_t len)
0019 {
0020 struct super_block *sb = dir->sb;
0021 unsigned int index, remain;
0022
0023 index = offset >> sb->s_blocksize_bits;
0024 offset &= sb->s_blocksize - 1;
0025 remain = sb->s_blocksize - offset;
0026 if (index + (remain < len) >= dir->nr_buffers)
0027 return -EINVAL;
0028
0029 if (remain < len) {
0030 memcpy(dst, dir->bhs[index]->b_data + offset, remain);
0031 dst += remain;
0032 len -= remain;
0033 index += 1;
0034 offset = 0;
0035 }
0036
0037 memcpy(dst, dir->bhs[index]->b_data + offset, len);
0038
0039 return 0;
0040 }
0041
0042 int adfs_dir_copyto(struct adfs_dir *dir, unsigned int offset, const void *src,
0043 size_t len)
0044 {
0045 struct super_block *sb = dir->sb;
0046 unsigned int index, remain;
0047
0048 index = offset >> sb->s_blocksize_bits;
0049 offset &= sb->s_blocksize - 1;
0050 remain = sb->s_blocksize - offset;
0051 if (index + (remain < len) >= dir->nr_buffers)
0052 return -EINVAL;
0053
0054 if (remain < len) {
0055 memcpy(dir->bhs[index]->b_data + offset, src, remain);
0056 src += remain;
0057 len -= remain;
0058 index += 1;
0059 offset = 0;
0060 }
0061
0062 memcpy(dir->bhs[index]->b_data + offset, src, len);
0063
0064 return 0;
0065 }
0066
0067 static void __adfs_dir_cleanup(struct adfs_dir *dir)
0068 {
0069 dir->nr_buffers = 0;
0070
0071 if (dir->bhs != dir->bh)
0072 kfree(dir->bhs);
0073 dir->bhs = NULL;
0074 dir->sb = NULL;
0075 }
0076
0077 void adfs_dir_relse(struct adfs_dir *dir)
0078 {
0079 unsigned int i;
0080
0081 for (i = 0; i < dir->nr_buffers; i++)
0082 brelse(dir->bhs[i]);
0083
0084 __adfs_dir_cleanup(dir);
0085 }
0086
0087 static void adfs_dir_forget(struct adfs_dir *dir)
0088 {
0089 unsigned int i;
0090
0091 for (i = 0; i < dir->nr_buffers; i++)
0092 bforget(dir->bhs[i]);
0093
0094 __adfs_dir_cleanup(dir);
0095 }
0096
0097 int adfs_dir_read_buffers(struct super_block *sb, u32 indaddr,
0098 unsigned int size, struct adfs_dir *dir)
0099 {
0100 struct buffer_head **bhs;
0101 unsigned int i, num;
0102 int block;
0103
0104 num = ALIGN(size, sb->s_blocksize) >> sb->s_blocksize_bits;
0105 if (num > ARRAY_SIZE(dir->bh)) {
0106
0107 if (dir->bhs != dir->bh)
0108 return -EINVAL;
0109
0110 bhs = kcalloc(num, sizeof(*bhs), GFP_KERNEL);
0111 if (!bhs)
0112 return -ENOMEM;
0113
0114 if (dir->nr_buffers)
0115 memcpy(bhs, dir->bhs, dir->nr_buffers * sizeof(*bhs));
0116
0117 dir->bhs = bhs;
0118 }
0119
0120 for (i = dir->nr_buffers; i < num; i++) {
0121 block = __adfs_block_map(sb, indaddr, i);
0122 if (!block) {
0123 adfs_error(sb, "dir %06x has a hole at offset %u",
0124 indaddr, i);
0125 goto error;
0126 }
0127
0128 dir->bhs[i] = sb_bread(sb, block);
0129 if (!dir->bhs[i]) {
0130 adfs_error(sb,
0131 "dir %06x failed read at offset %u, mapped block 0x%08x",
0132 indaddr, i, block);
0133 goto error;
0134 }
0135
0136 dir->nr_buffers++;
0137 }
0138 return 0;
0139
0140 error:
0141 adfs_dir_relse(dir);
0142
0143 return -EIO;
0144 }
0145
0146 static int adfs_dir_read(struct super_block *sb, u32 indaddr,
0147 unsigned int size, struct adfs_dir *dir)
0148 {
0149 dir->sb = sb;
0150 dir->bhs = dir->bh;
0151 dir->nr_buffers = 0;
0152
0153 return ADFS_SB(sb)->s_dir->read(sb, indaddr, size, dir);
0154 }
0155
0156 static int adfs_dir_read_inode(struct super_block *sb, struct inode *inode,
0157 struct adfs_dir *dir)
0158 {
0159 int ret;
0160
0161 ret = adfs_dir_read(sb, ADFS_I(inode)->indaddr, inode->i_size, dir);
0162 if (ret)
0163 return ret;
0164
0165 if (ADFS_I(inode)->parent_id != dir->parent_id) {
0166 adfs_error(sb,
0167 "parent directory id changed under me! (%06x but got %06x)\n",
0168 ADFS_I(inode)->parent_id, dir->parent_id);
0169 adfs_dir_relse(dir);
0170 ret = -EIO;
0171 }
0172
0173 return ret;
0174 }
0175
0176 static void adfs_dir_mark_dirty(struct adfs_dir *dir)
0177 {
0178 unsigned int i;
0179
0180
0181 for (i = 0; i < dir->nr_buffers; i++)
0182 mark_buffer_dirty(dir->bhs[i]);
0183 }
0184
0185 static int adfs_dir_sync(struct adfs_dir *dir)
0186 {
0187 int err = 0;
0188 int i;
0189
0190 for (i = dir->nr_buffers - 1; i >= 0; i--) {
0191 struct buffer_head *bh = dir->bhs[i];
0192 sync_dirty_buffer(bh);
0193 if (buffer_req(bh) && !buffer_uptodate(bh))
0194 err = -EIO;
0195 }
0196
0197 return err;
0198 }
0199
0200 void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj)
0201 {
0202 unsigned int dots, i;
0203
0204
0205
0206
0207
0208
0209
0210
0211
0212
0213 for (i = dots = 0; i < obj->name_len; i++)
0214 if (obj->name[i] == '/') {
0215 obj->name[i] = '.';
0216 dots++;
0217 }
0218
0219 if (obj->name_len <= 2 && dots == obj->name_len)
0220 obj->name[0] = '^';
0221
0222
0223
0224
0225
0226 if (!(obj->attr & ADFS_NDA_DIRECTORY) && ADFS_SB(dir->sb)->s_ftsuffix) {
0227 u16 filetype = adfs_filetype(obj->loadaddr);
0228
0229 if (filetype != ADFS_FILETYPE_NONE) {
0230 obj->name[obj->name_len++] = ',';
0231 obj->name[obj->name_len++] = hex_asc_lo(filetype >> 8);
0232 obj->name[obj->name_len++] = hex_asc_lo(filetype >> 4);
0233 obj->name[obj->name_len++] = hex_asc_lo(filetype >> 0);
0234 }
0235 }
0236 }
0237
0238 static int adfs_iterate(struct file *file, struct dir_context *ctx)
0239 {
0240 struct inode *inode = file_inode(file);
0241 struct super_block *sb = inode->i_sb;
0242 const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
0243 struct adfs_dir dir;
0244 int ret;
0245
0246 down_read(&adfs_dir_rwsem);
0247 ret = adfs_dir_read_inode(sb, inode, &dir);
0248 if (ret)
0249 goto unlock;
0250
0251 if (ctx->pos == 0) {
0252 if (!dir_emit_dot(file, ctx))
0253 goto unlock_relse;
0254 ctx->pos = 1;
0255 }
0256 if (ctx->pos == 1) {
0257 if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR))
0258 goto unlock_relse;
0259 ctx->pos = 2;
0260 }
0261
0262 ret = ops->iterate(&dir, ctx);
0263
0264 unlock_relse:
0265 up_read(&adfs_dir_rwsem);
0266 adfs_dir_relse(&dir);
0267 return ret;
0268
0269 unlock:
0270 up_read(&adfs_dir_rwsem);
0271 return ret;
0272 }
0273
0274 int
0275 adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
0276 {
0277 const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
0278 struct adfs_dir dir;
0279 int ret;
0280
0281 if (!IS_ENABLED(CONFIG_ADFS_FS_RW))
0282 return -EINVAL;
0283
0284 if (!ops->update)
0285 return -EINVAL;
0286
0287 down_write(&adfs_dir_rwsem);
0288 ret = adfs_dir_read(sb, obj->parent_id, 0, &dir);
0289 if (ret)
0290 goto unlock;
0291
0292 ret = ops->update(&dir, obj);
0293 if (ret)
0294 goto forget;
0295
0296 ret = ops->commit(&dir);
0297 if (ret)
0298 goto forget;
0299 up_write(&adfs_dir_rwsem);
0300
0301 adfs_dir_mark_dirty(&dir);
0302
0303 if (wait)
0304 ret = adfs_dir_sync(&dir);
0305
0306 adfs_dir_relse(&dir);
0307 return ret;
0308
0309
0310
0311
0312
0313
0314 forget:
0315 if (ret == -ENOENT)
0316 adfs_dir_relse(&dir);
0317 else
0318 adfs_dir_forget(&dir);
0319 unlock:
0320 up_write(&adfs_dir_rwsem);
0321
0322 return ret;
0323 }
0324
0325 static unsigned char adfs_tolower(unsigned char c)
0326 {
0327 if (c >= 'A' && c <= 'Z')
0328 c += 'a' - 'A';
0329 return c;
0330 }
0331
0332 static int __adfs_compare(const unsigned char *qstr, u32 qlen,
0333 const char *str, u32 len)
0334 {
0335 u32 i;
0336
0337 if (qlen != len)
0338 return 1;
0339
0340 for (i = 0; i < qlen; i++)
0341 if (adfs_tolower(qstr[i]) != adfs_tolower(str[i]))
0342 return 1;
0343
0344 return 0;
0345 }
0346
0347 static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,
0348 struct object_info *obj)
0349 {
0350 struct super_block *sb = inode->i_sb;
0351 const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
0352 const unsigned char *name;
0353 struct adfs_dir dir;
0354 u32 name_len;
0355 int ret;
0356
0357 down_read(&adfs_dir_rwsem);
0358 ret = adfs_dir_read_inode(sb, inode, &dir);
0359 if (ret)
0360 goto unlock;
0361
0362 ret = ops->setpos(&dir, 0);
0363 if (ret)
0364 goto unlock_relse;
0365
0366 ret = -ENOENT;
0367 name = qstr->name;
0368 name_len = qstr->len;
0369 while (ops->getnext(&dir, obj) == 0) {
0370 if (!__adfs_compare(name, name_len, obj->name, obj->name_len)) {
0371 ret = 0;
0372 break;
0373 }
0374 }
0375 obj->parent_id = ADFS_I(inode)->indaddr;
0376
0377 unlock_relse:
0378 up_read(&adfs_dir_rwsem);
0379 adfs_dir_relse(&dir);
0380 return ret;
0381
0382 unlock:
0383 up_read(&adfs_dir_rwsem);
0384 return ret;
0385 }
0386
0387 const struct file_operations adfs_dir_operations = {
0388 .read = generic_read_dir,
0389 .llseek = generic_file_llseek,
0390 .iterate_shared = adfs_iterate,
0391 .fsync = generic_file_fsync,
0392 };
0393
0394 static int
0395 adfs_hash(const struct dentry *parent, struct qstr *qstr)
0396 {
0397 const unsigned char *name;
0398 unsigned long hash;
0399 u32 len;
0400
0401 if (qstr->len > ADFS_SB(parent->d_sb)->s_namelen)
0402 return -ENAMETOOLONG;
0403
0404 len = qstr->len;
0405 name = qstr->name;
0406 hash = init_name_hash(parent);
0407 while (len--)
0408 hash = partial_name_hash(adfs_tolower(*name++), hash);
0409 qstr->hash = end_name_hash(hash);
0410
0411 return 0;
0412 }
0413
0414
0415
0416
0417
0418 static int adfs_compare(const struct dentry *dentry, unsigned int len,
0419 const char *str, const struct qstr *qstr)
0420 {
0421 return __adfs_compare(qstr->name, qstr->len, str, len);
0422 }
0423
0424 const struct dentry_operations adfs_dentry_operations = {
0425 .d_hash = adfs_hash,
0426 .d_compare = adfs_compare,
0427 };
0428
0429 static struct dentry *
0430 adfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
0431 {
0432 struct inode *inode = NULL;
0433 struct object_info obj;
0434 int error;
0435
0436 error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
0437 if (error == 0) {
0438
0439
0440
0441
0442 inode = adfs_iget(dir->i_sb, &obj);
0443 if (!inode)
0444 inode = ERR_PTR(-EACCES);
0445 } else if (error != -ENOENT) {
0446 inode = ERR_PTR(error);
0447 }
0448 return d_splice_alias(inode, dentry);
0449 }
0450
0451
0452
0453
0454 const struct inode_operations adfs_dir_inode_operations = {
0455 .lookup = adfs_lookup,
0456 .setattr = adfs_notify_change,
0457 };