| .. | .. | 
|---|
 | 1 | +// SPDX-License-Identifier: GPL-2.0-only  | 
|---|
| 1 | 2 |  /* | 
|---|
| 2 | 3 |   *  linux/fs/adfs/dir.c | 
|---|
| 3 | 4 |   * | 
|---|
| 4 | 5 |   *  Copyright (C) 1999-2000 Russell King | 
|---|
| 5 | 6 |   * | 
|---|
| 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 | 7 |   *  Common directory handling for ADFS | 
|---|
| 11 | 8 |   */ | 
|---|
 | 9 | +#include <linux/slab.h>  | 
|---|
| 12 | 10 |  #include "adfs.h" | 
|---|
| 13 | 11 |   | 
|---|
| 14 | 12 |  /* | 
|---|
| 15 | 13 |   * For future.  This should probably be per-directory. | 
|---|
| 16 | 14 |   */ | 
|---|
| 17 |  | -static DEFINE_RWLOCK(adfs_dir_lock);  | 
|---|
 | 15 | +static DECLARE_RWSEM(adfs_dir_rwsem);  | 
|---|
| 18 | 16 |   | 
|---|
| 19 |  | -static int  | 
|---|
| 20 |  | -adfs_readdir(struct file *file, struct dir_context *ctx)  | 
|---|
 | 17 | +int adfs_dir_copyfrom(void *dst, struct adfs_dir *dir, unsigned int offset,  | 
|---|
 | 18 | +		      size_t len)  | 
|---|
 | 19 | +{  | 
|---|
 | 20 | +	struct super_block *sb = dir->sb;  | 
|---|
 | 21 | +	unsigned int index, remain;  | 
|---|
 | 22 | +  | 
|---|
 | 23 | +	index = offset >> sb->s_blocksize_bits;  | 
|---|
 | 24 | +	offset &= sb->s_blocksize - 1;  | 
|---|
 | 25 | +	remain = sb->s_blocksize - offset;  | 
|---|
 | 26 | +	if (index + (remain < len) >= dir->nr_buffers)  | 
|---|
 | 27 | +		return -EINVAL;  | 
|---|
 | 28 | +  | 
|---|
 | 29 | +	if (remain < len) {  | 
|---|
 | 30 | +		memcpy(dst, dir->bhs[index]->b_data + offset, remain);  | 
|---|
 | 31 | +		dst += remain;  | 
|---|
 | 32 | +		len -= remain;  | 
|---|
 | 33 | +		index += 1;  | 
|---|
 | 34 | +		offset = 0;  | 
|---|
 | 35 | +	}  | 
|---|
 | 36 | +  | 
|---|
 | 37 | +	memcpy(dst, dir->bhs[index]->b_data + offset, len);  | 
|---|
 | 38 | +  | 
|---|
 | 39 | +	return 0;  | 
|---|
 | 40 | +}  | 
|---|
 | 41 | +  | 
|---|
 | 42 | +int adfs_dir_copyto(struct adfs_dir *dir, unsigned int offset, const void *src,  | 
|---|
 | 43 | +		    size_t len)  | 
|---|
 | 44 | +{  | 
|---|
 | 45 | +	struct super_block *sb = dir->sb;  | 
|---|
 | 46 | +	unsigned int index, remain;  | 
|---|
 | 47 | +  | 
|---|
 | 48 | +	index = offset >> sb->s_blocksize_bits;  | 
|---|
 | 49 | +	offset &= sb->s_blocksize - 1;  | 
|---|
 | 50 | +	remain = sb->s_blocksize - offset;  | 
|---|
 | 51 | +	if (index + (remain < len) >= dir->nr_buffers)  | 
|---|
 | 52 | +		return -EINVAL;  | 
|---|
 | 53 | +  | 
|---|
 | 54 | +	if (remain < len) {  | 
|---|
 | 55 | +		memcpy(dir->bhs[index]->b_data + offset, src, remain);  | 
|---|
 | 56 | +		src += remain;  | 
|---|
 | 57 | +		len -= remain;  | 
|---|
 | 58 | +		index += 1;  | 
|---|
 | 59 | +		offset = 0;  | 
|---|
 | 60 | +	}  | 
|---|
 | 61 | +  | 
|---|
 | 62 | +	memcpy(dir->bhs[index]->b_data + offset, src, len);  | 
|---|
 | 63 | +  | 
|---|
 | 64 | +	return 0;  | 
|---|
 | 65 | +}  | 
|---|
 | 66 | +  | 
|---|
 | 67 | +static void __adfs_dir_cleanup(struct adfs_dir *dir)  | 
|---|
 | 68 | +{  | 
|---|
 | 69 | +	dir->nr_buffers = 0;  | 
|---|
 | 70 | +  | 
|---|
 | 71 | +	if (dir->bhs != dir->bh)  | 
|---|
 | 72 | +		kfree(dir->bhs);  | 
|---|
 | 73 | +	dir->bhs = NULL;  | 
|---|
 | 74 | +	dir->sb = NULL;  | 
|---|
 | 75 | +}  | 
|---|
 | 76 | +  | 
|---|
 | 77 | +void adfs_dir_relse(struct adfs_dir *dir)  | 
|---|
 | 78 | +{  | 
|---|
 | 79 | +	unsigned int i;  | 
|---|
 | 80 | +  | 
|---|
 | 81 | +	for (i = 0; i < dir->nr_buffers; i++)  | 
|---|
 | 82 | +		brelse(dir->bhs[i]);  | 
|---|
 | 83 | +  | 
|---|
 | 84 | +	__adfs_dir_cleanup(dir);  | 
|---|
 | 85 | +}  | 
|---|
 | 86 | +  | 
|---|
 | 87 | +static void adfs_dir_forget(struct adfs_dir *dir)  | 
|---|
 | 88 | +{  | 
|---|
 | 89 | +	unsigned int i;  | 
|---|
 | 90 | +  | 
|---|
 | 91 | +	for (i = 0; i < dir->nr_buffers; i++)  | 
|---|
 | 92 | +		bforget(dir->bhs[i]);  | 
|---|
 | 93 | +  | 
|---|
 | 94 | +	__adfs_dir_cleanup(dir);  | 
|---|
 | 95 | +}  | 
|---|
 | 96 | +  | 
|---|
 | 97 | +int adfs_dir_read_buffers(struct super_block *sb, u32 indaddr,  | 
|---|
 | 98 | +			  unsigned int size, struct adfs_dir *dir)  | 
|---|
 | 99 | +{  | 
|---|
 | 100 | +	struct buffer_head **bhs;  | 
|---|
 | 101 | +	unsigned int i, num;  | 
|---|
 | 102 | +	int block;  | 
|---|
 | 103 | +  | 
|---|
 | 104 | +	num = ALIGN(size, sb->s_blocksize) >> sb->s_blocksize_bits;  | 
|---|
 | 105 | +	if (num > ARRAY_SIZE(dir->bh)) {  | 
|---|
 | 106 | +		/* We only allow one extension */  | 
|---|
 | 107 | +		if (dir->bhs != dir->bh)  | 
|---|
 | 108 | +			return -EINVAL;  | 
|---|
 | 109 | +  | 
|---|
 | 110 | +		bhs = kcalloc(num, sizeof(*bhs), GFP_KERNEL);  | 
|---|
 | 111 | +		if (!bhs)  | 
|---|
 | 112 | +			return -ENOMEM;  | 
|---|
 | 113 | +  | 
|---|
 | 114 | +		if (dir->nr_buffers)  | 
|---|
 | 115 | +			memcpy(bhs, dir->bhs, dir->nr_buffers * sizeof(*bhs));  | 
|---|
 | 116 | +  | 
|---|
 | 117 | +		dir->bhs = bhs;  | 
|---|
 | 118 | +	}  | 
|---|
 | 119 | +  | 
|---|
 | 120 | +	for (i = dir->nr_buffers; i < num; i++) {  | 
|---|
 | 121 | +		block = __adfs_block_map(sb, indaddr, i);  | 
|---|
 | 122 | +		if (!block) {  | 
|---|
 | 123 | +			adfs_error(sb, "dir %06x has a hole at offset %u",  | 
|---|
 | 124 | +				   indaddr, i);  | 
|---|
 | 125 | +			goto error;  | 
|---|
 | 126 | +		}  | 
|---|
 | 127 | +  | 
|---|
 | 128 | +		dir->bhs[i] = sb_bread(sb, block);  | 
|---|
 | 129 | +		if (!dir->bhs[i]) {  | 
|---|
 | 130 | +			adfs_error(sb,  | 
|---|
 | 131 | +				   "dir %06x failed read at offset %u, mapped block 0x%08x",  | 
|---|
 | 132 | +				   indaddr, i, block);  | 
|---|
 | 133 | +			goto error;  | 
|---|
 | 134 | +		}  | 
|---|
 | 135 | +  | 
|---|
 | 136 | +		dir->nr_buffers++;  | 
|---|
 | 137 | +	}  | 
|---|
 | 138 | +	return 0;  | 
|---|
 | 139 | +  | 
|---|
 | 140 | +error:  | 
|---|
 | 141 | +	adfs_dir_relse(dir);  | 
|---|
 | 142 | +  | 
|---|
 | 143 | +	return -EIO;  | 
|---|
 | 144 | +}  | 
|---|
 | 145 | +  | 
|---|
 | 146 | +static int adfs_dir_read(struct super_block *sb, u32 indaddr,  | 
|---|
 | 147 | +			 unsigned int size, struct adfs_dir *dir)  | 
|---|
 | 148 | +{  | 
|---|
 | 149 | +	dir->sb = sb;  | 
|---|
 | 150 | +	dir->bhs = dir->bh;  | 
|---|
 | 151 | +	dir->nr_buffers = 0;  | 
|---|
 | 152 | +  | 
|---|
 | 153 | +	return ADFS_SB(sb)->s_dir->read(sb, indaddr, size, dir);  | 
|---|
 | 154 | +}  | 
|---|
 | 155 | +  | 
|---|
 | 156 | +static int adfs_dir_read_inode(struct super_block *sb, struct inode *inode,  | 
|---|
 | 157 | +			       struct adfs_dir *dir)  | 
|---|
 | 158 | +{  | 
|---|
 | 159 | +	int ret;  | 
|---|
 | 160 | +  | 
|---|
 | 161 | +	ret = adfs_dir_read(sb, ADFS_I(inode)->indaddr, inode->i_size, dir);  | 
|---|
 | 162 | +	if (ret)  | 
|---|
 | 163 | +		return ret;  | 
|---|
 | 164 | +  | 
|---|
 | 165 | +	if (ADFS_I(inode)->parent_id != dir->parent_id) {  | 
|---|
 | 166 | +		adfs_error(sb,  | 
|---|
 | 167 | +			   "parent directory id changed under me! (%06x but got %06x)\n",  | 
|---|
 | 168 | +			   ADFS_I(inode)->parent_id, dir->parent_id);  | 
|---|
 | 169 | +		adfs_dir_relse(dir);  | 
|---|
 | 170 | +		ret = -EIO;  | 
|---|
 | 171 | +	}  | 
|---|
 | 172 | +  | 
|---|
 | 173 | +	return ret;  | 
|---|
 | 174 | +}  | 
|---|
 | 175 | +  | 
|---|
 | 176 | +static void adfs_dir_mark_dirty(struct adfs_dir *dir)  | 
|---|
 | 177 | +{  | 
|---|
 | 178 | +	unsigned int i;  | 
|---|
 | 179 | +  | 
|---|
 | 180 | +	/* Mark the buffers dirty */  | 
|---|
 | 181 | +	for (i = 0; i < dir->nr_buffers; i++)  | 
|---|
 | 182 | +		mark_buffer_dirty(dir->bhs[i]);  | 
|---|
 | 183 | +}  | 
|---|
 | 184 | +  | 
|---|
 | 185 | +static int adfs_dir_sync(struct adfs_dir *dir)  | 
|---|
 | 186 | +{  | 
|---|
 | 187 | +	int err = 0;  | 
|---|
 | 188 | +	int i;  | 
|---|
 | 189 | +  | 
|---|
 | 190 | +	for (i = dir->nr_buffers - 1; i >= 0; i--) {  | 
|---|
 | 191 | +		struct buffer_head *bh = dir->bhs[i];  | 
|---|
 | 192 | +		sync_dirty_buffer(bh);  | 
|---|
 | 193 | +		if (buffer_req(bh) && !buffer_uptodate(bh))  | 
|---|
 | 194 | +			err = -EIO;  | 
|---|
 | 195 | +	}  | 
|---|
 | 196 | +  | 
|---|
 | 197 | +	return err;  | 
|---|
 | 198 | +}  | 
|---|
 | 199 | +  | 
|---|
 | 200 | +void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj)  | 
|---|
 | 201 | +{  | 
|---|
 | 202 | +	unsigned int dots, i;  | 
|---|
 | 203 | +  | 
|---|
 | 204 | +	/*  | 
|---|
 | 205 | +	 * RISC OS allows the use of '/' in directory entry names, so we need  | 
|---|
 | 206 | +	 * to fix these up.  '/' is typically used for FAT compatibility to  | 
|---|
 | 207 | +	 * represent '.', so do the same conversion here.  In any case, '.'  | 
|---|
 | 208 | +	 * will never be in a RISC OS name since it is used as the pathname  | 
|---|
 | 209 | +	 * separator.  Handle the case where we may generate a '.' or '..'  | 
|---|
 | 210 | +	 * name, replacing the first character with '^' (the RISC OS "parent  | 
|---|
 | 211 | +	 * directory" character.)  | 
|---|
 | 212 | +	 */  | 
|---|
 | 213 | +	for (i = dots = 0; i < obj->name_len; i++)  | 
|---|
 | 214 | +		if (obj->name[i] == '/') {  | 
|---|
 | 215 | +			obj->name[i] = '.';  | 
|---|
 | 216 | +			dots++;  | 
|---|
 | 217 | +		}  | 
|---|
 | 218 | +  | 
|---|
 | 219 | +	if (obj->name_len <= 2 && dots == obj->name_len)  | 
|---|
 | 220 | +		obj->name[0] = '^';  | 
|---|
 | 221 | +  | 
|---|
 | 222 | +	/*  | 
|---|
 | 223 | +	 * If the object is a file, and the user requested the ,xyz hex  | 
|---|
 | 224 | +	 * filetype suffix to the name, check the filetype and append.  | 
|---|
 | 225 | +	 */  | 
|---|
 | 226 | +	if (!(obj->attr & ADFS_NDA_DIRECTORY) && ADFS_SB(dir->sb)->s_ftsuffix) {  | 
|---|
 | 227 | +		u16 filetype = adfs_filetype(obj->loadaddr);  | 
|---|
 | 228 | +  | 
|---|
 | 229 | +		if (filetype != ADFS_FILETYPE_NONE) {  | 
|---|
 | 230 | +			obj->name[obj->name_len++] = ',';  | 
|---|
 | 231 | +			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 8);  | 
|---|
 | 232 | +			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 4);  | 
|---|
 | 233 | +			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 0);  | 
|---|
 | 234 | +		}  | 
|---|
 | 235 | +	}  | 
|---|
 | 236 | +}  | 
|---|
 | 237 | +  | 
|---|
 | 238 | +static int adfs_iterate(struct file *file, struct dir_context *ctx)  | 
|---|
| 21 | 239 |  { | 
|---|
| 22 | 240 |  	struct inode *inode = file_inode(file); | 
|---|
| 23 | 241 |  	struct super_block *sb = inode->i_sb; | 
|---|
| 24 | 242 |  	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; | 
|---|
| 25 |  | -	struct object_info obj;  | 
|---|
| 26 | 243 |  	struct adfs_dir dir; | 
|---|
| 27 |  | -	int ret = 0;  | 
|---|
 | 244 | +	int ret;  | 
|---|
| 28 | 245 |   | 
|---|
| 29 |  | -	if (ctx->pos >> 32)  | 
|---|
| 30 |  | -		return 0;  | 
|---|
| 31 |  | -  | 
|---|
| 32 |  | -	ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);  | 
|---|
 | 246 | +	down_read(&adfs_dir_rwsem);  | 
|---|
 | 247 | +	ret = adfs_dir_read_inode(sb, inode, &dir);  | 
|---|
| 33 | 248 |  	if (ret) | 
|---|
| 34 |  | -		return ret;  | 
|---|
 | 249 | +		goto unlock;  | 
|---|
| 35 | 250 |   | 
|---|
| 36 | 251 |  	if (ctx->pos == 0) { | 
|---|
| 37 | 252 |  		if (!dir_emit_dot(file, ctx)) | 
|---|
| 38 |  | -			goto free_out;  | 
|---|
 | 253 | +			goto unlock_relse;  | 
|---|
| 39 | 254 |  		ctx->pos = 1; | 
|---|
| 40 | 255 |  	} | 
|---|
| 41 | 256 |  	if (ctx->pos == 1) { | 
|---|
| 42 | 257 |  		if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR)) | 
|---|
| 43 |  | -			goto free_out;  | 
|---|
 | 258 | +			goto unlock_relse;  | 
|---|
| 44 | 259 |  		ctx->pos = 2; | 
|---|
| 45 | 260 |  	} | 
|---|
| 46 | 261 |   | 
|---|
| 47 |  | -	read_lock(&adfs_dir_lock);  | 
|---|
 | 262 | +	ret = ops->iterate(&dir, ctx);  | 
|---|
| 48 | 263 |   | 
|---|
| 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 |  | -	}  | 
|---|
 | 264 | +unlock_relse:  | 
|---|
 | 265 | +	up_read(&adfs_dir_rwsem);  | 
|---|
 | 266 | +	adfs_dir_relse(&dir);  | 
|---|
 | 267 | +	return ret;  | 
|---|
| 58 | 268 |   | 
|---|
| 59 |  | -unlock_out:  | 
|---|
| 60 |  | -	read_unlock(&adfs_dir_lock);  | 
|---|
| 61 |  | -  | 
|---|
| 62 |  | -free_out:  | 
|---|
| 63 |  | -	ops->free(&dir);  | 
|---|
 | 269 | +unlock:  | 
|---|
 | 270 | +	up_read(&adfs_dir_rwsem);  | 
|---|
| 64 | 271 |  	return ret; | 
|---|
| 65 | 272 |  } | 
|---|
| 66 | 273 |   | 
|---|
| 67 | 274 |  int | 
|---|
| 68 | 275 |  adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait) | 
|---|
| 69 | 276 |  { | 
|---|
| 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 | 277 |  	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir; | 
|---|
| 133 | 278 |  	struct adfs_dir dir; | 
|---|
| 134 | 279 |  	int ret; | 
|---|
| 135 | 280 |   | 
|---|
| 136 |  | -	ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);  | 
|---|
 | 281 | +	if (!IS_ENABLED(CONFIG_ADFS_FS_RW))  | 
|---|
 | 282 | +		return -EINVAL;  | 
|---|
 | 283 | +  | 
|---|
 | 284 | +	if (!ops->update)  | 
|---|
 | 285 | +		return -EINVAL;  | 
|---|
 | 286 | +  | 
|---|
 | 287 | +	down_write(&adfs_dir_rwsem);  | 
|---|
 | 288 | +	ret = adfs_dir_read(sb, obj->parent_id, 0, &dir);  | 
|---|
| 137 | 289 |  	if (ret) | 
|---|
| 138 |  | -		goto out;  | 
|---|
 | 290 | +		goto unlock;  | 
|---|
| 139 | 291 |   | 
|---|
| 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 |  | -	}  | 
|---|
 | 292 | +	ret = ops->update(&dir, obj);  | 
|---|
 | 293 | +	if (ret)  | 
|---|
 | 294 | +		goto forget;  | 
|---|
| 146 | 295 |   | 
|---|
| 147 |  | -	obj->parent_id = inode->i_ino;  | 
|---|
 | 296 | +	ret = ops->commit(&dir);  | 
|---|
 | 297 | +	if (ret)  | 
|---|
 | 298 | +		goto forget;  | 
|---|
 | 299 | +	up_write(&adfs_dir_rwsem);  | 
|---|
| 148 | 300 |   | 
|---|
| 149 |  | -	read_lock(&adfs_dir_lock);  | 
|---|
 | 301 | +	adfs_dir_mark_dirty(&dir);  | 
|---|
 | 302 | +  | 
|---|
 | 303 | +	if (wait)  | 
|---|
 | 304 | +		ret = adfs_dir_sync(&dir);  | 
|---|
 | 305 | +  | 
|---|
 | 306 | +	adfs_dir_relse(&dir);  | 
|---|
 | 307 | +	return ret;  | 
|---|
 | 308 | +  | 
|---|
 | 309 | +	/*  | 
|---|
 | 310 | +	 * If the updated failed because the entry wasn't found, we can  | 
|---|
 | 311 | +	 * just release the buffers. If it was any other error, forget  | 
|---|
 | 312 | +	 * the dirtied buffers so they aren't written back to the media.  | 
|---|
 | 313 | +	 */  | 
|---|
 | 314 | +forget:  | 
|---|
 | 315 | +	if (ret == -ENOENT)  | 
|---|
 | 316 | +		adfs_dir_relse(&dir);  | 
|---|
 | 317 | +	else  | 
|---|
 | 318 | +		adfs_dir_forget(&dir);  | 
|---|
 | 319 | +unlock:  | 
|---|
 | 320 | +	up_write(&adfs_dir_rwsem);  | 
|---|
 | 321 | +  | 
|---|
 | 322 | +	return ret;  | 
|---|
 | 323 | +}  | 
|---|
 | 324 | +  | 
|---|
 | 325 | +static unsigned char adfs_tolower(unsigned char c)  | 
|---|
 | 326 | +{  | 
|---|
 | 327 | +	if (c >= 'A' && c <= 'Z')  | 
|---|
 | 328 | +		c += 'a' - 'A';  | 
|---|
 | 329 | +	return c;  | 
|---|
 | 330 | +}  | 
|---|
 | 331 | +  | 
|---|
 | 332 | +static int __adfs_compare(const unsigned char *qstr, u32 qlen,  | 
|---|
 | 333 | +			  const char *str, u32 len)  | 
|---|
 | 334 | +{  | 
|---|
 | 335 | +	u32 i;  | 
|---|
 | 336 | +  | 
|---|
 | 337 | +	if (qlen != len)  | 
|---|
 | 338 | +		return 1;  | 
|---|
 | 339 | +  | 
|---|
 | 340 | +	for (i = 0; i < qlen; i++)  | 
|---|
 | 341 | +		if (adfs_tolower(qstr[i]) != adfs_tolower(str[i]))  | 
|---|
 | 342 | +			return 1;  | 
|---|
 | 343 | +  | 
|---|
 | 344 | +	return 0;  | 
|---|
 | 345 | +}  | 
|---|
 | 346 | +  | 
|---|
 | 347 | +static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,  | 
|---|
 | 348 | +				  struct object_info *obj)  | 
|---|
 | 349 | +{  | 
|---|
 | 350 | +	struct super_block *sb = inode->i_sb;  | 
|---|
 | 351 | +	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;  | 
|---|
 | 352 | +	const unsigned char *name;  | 
|---|
 | 353 | +	struct adfs_dir dir;  | 
|---|
 | 354 | +	u32 name_len;  | 
|---|
 | 355 | +	int ret;  | 
|---|
 | 356 | +  | 
|---|
 | 357 | +	down_read(&adfs_dir_rwsem);  | 
|---|
 | 358 | +	ret = adfs_dir_read_inode(sb, inode, &dir);  | 
|---|
 | 359 | +	if (ret)  | 
|---|
 | 360 | +		goto unlock;  | 
|---|
| 150 | 361 |   | 
|---|
| 151 | 362 |  	ret = ops->setpos(&dir, 0); | 
|---|
| 152 | 363 |  	if (ret) | 
|---|
| 153 |  | -		goto unlock_out;  | 
|---|
 | 364 | +		goto unlock_relse;  | 
|---|
| 154 | 365 |   | 
|---|
| 155 | 366 |  	ret = -ENOENT; | 
|---|
 | 367 | +	name = qstr->name;  | 
|---|
 | 368 | +	name_len = qstr->len;  | 
|---|
| 156 | 369 |  	while (ops->getnext(&dir, obj) == 0) { | 
|---|
| 157 |  | -		if (adfs_match(name, obj)) {  | 
|---|
 | 370 | +		if (!__adfs_compare(name, name_len, obj->name, obj->name_len)) {  | 
|---|
| 158 | 371 |  			ret = 0; | 
|---|
| 159 | 372 |  			break; | 
|---|
| 160 | 373 |  		} | 
|---|
| 161 | 374 |  	} | 
|---|
 | 375 | +	obj->parent_id = ADFS_I(inode)->indaddr;  | 
|---|
| 162 | 376 |   | 
|---|
| 163 |  | -unlock_out:  | 
|---|
| 164 |  | -	read_unlock(&adfs_dir_lock);  | 
|---|
 | 377 | +unlock_relse:  | 
|---|
 | 378 | +	up_read(&adfs_dir_rwsem);  | 
|---|
 | 379 | +	adfs_dir_relse(&dir);  | 
|---|
 | 380 | +	return ret;  | 
|---|
| 165 | 381 |   | 
|---|
| 166 |  | -free_out:  | 
|---|
| 167 |  | -	ops->free(&dir);  | 
|---|
| 168 |  | -out:  | 
|---|
 | 382 | +unlock:  | 
|---|
 | 383 | +	up_read(&adfs_dir_rwsem);  | 
|---|
| 169 | 384 |  	return ret; | 
|---|
| 170 | 385 |  } | 
|---|
| 171 | 386 |   | 
|---|
| 172 | 387 |  const struct file_operations adfs_dir_operations = { | 
|---|
| 173 | 388 |  	.read		= generic_read_dir, | 
|---|
| 174 | 389 |  	.llseek		= generic_file_llseek, | 
|---|
| 175 |  | -	.iterate	= adfs_readdir,  | 
|---|
 | 390 | +	.iterate_shared	= adfs_iterate,  | 
|---|
| 176 | 391 |  	.fsync		= generic_file_fsync, | 
|---|
| 177 | 392 |  }; | 
|---|
| 178 | 393 |   | 
|---|
| 179 | 394 |  static int | 
|---|
| 180 | 395 |  adfs_hash(const struct dentry *parent, struct qstr *qstr) | 
|---|
| 181 | 396 |  { | 
|---|
| 182 |  | -	const unsigned int name_len = ADFS_SB(parent->d_sb)->s_namelen;  | 
|---|
| 183 | 397 |  	const unsigned char *name; | 
|---|
| 184 | 398 |  	unsigned long hash; | 
|---|
| 185 |  | -	int i;  | 
|---|
 | 399 | +	u32 len;  | 
|---|
| 186 | 400 |   | 
|---|
| 187 |  | -	if (qstr->len < name_len)  | 
|---|
| 188 |  | -		return 0;  | 
|---|
 | 401 | +	if (qstr->len > ADFS_SB(parent->d_sb)->s_namelen)  | 
|---|
 | 402 | +		return -ENAMETOOLONG;  | 
|---|
| 189 | 403 |   | 
|---|
| 190 |  | -	/*  | 
|---|
| 191 |  | -	 * Truncate the name in place, avoids  | 
|---|
| 192 |  | -	 * having to define a compare function.  | 
|---|
| 193 |  | -	 */  | 
|---|
| 194 |  | -	qstr->len = i = name_len;  | 
|---|
 | 404 | +	len = qstr->len;  | 
|---|
| 195 | 405 |  	name = qstr->name; | 
|---|
| 196 | 406 |  	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 |  | -	}  | 
|---|
 | 407 | +	while (len--)  | 
|---|
 | 408 | +		hash = partial_name_hash(adfs_tolower(*name++), hash);  | 
|---|
| 206 | 409 |  	qstr->hash = end_name_hash(hash); | 
|---|
| 207 | 410 |   | 
|---|
| 208 | 411 |  	return 0; | 
|---|
| .. | .. | 
|---|
| 212 | 415 |   * Compare two names, taking note of the name length | 
|---|
| 213 | 416 |   * requirements of the underlying filesystem. | 
|---|
| 214 | 417 |   */ | 
|---|
| 215 |  | -static int  | 
|---|
| 216 |  | -adfs_compare(const struct dentry *dentry,  | 
|---|
| 217 |  | -		unsigned int len, const char *str, const struct qstr *name)  | 
|---|
 | 418 | +static int adfs_compare(const struct dentry *dentry, unsigned int len,  | 
|---|
 | 419 | +			const char *str, const struct qstr *qstr)  | 
|---|
| 218 | 420 |  { | 
|---|
| 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;  | 
|---|
 | 421 | +	return __adfs_compare(qstr->name, qstr->len, str, len);  | 
|---|
| 239 | 422 |  } | 
|---|
| 240 | 423 |   | 
|---|
| 241 | 424 |  const struct dentry_operations adfs_dentry_operations = { | 
|---|