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