xref: /openbmc/linux/fs/adfs/dir.c (revision c2cd9d04)
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 void adfs_object_fixup(struct adfs_dir *dir, struct object_info *obj)
20 {
21 	unsigned int dots, i;
22 
23 	/*
24 	 * RISC OS allows the use of '/' in directory entry names, so we need
25 	 * to fix these up.  '/' is typically used for FAT compatibility to
26 	 * represent '.', so do the same conversion here.  In any case, '.'
27 	 * will never be in a RISC OS name since it is used as the pathname
28 	 * separator.  Handle the case where we may generate a '.' or '..'
29 	 * name, replacing the first character with '^' (the RISC OS "parent
30 	 * directory" character.)
31 	 */
32 	for (i = dots = 0; i < obj->name_len; i++)
33 		if (obj->name[i] == '/') {
34 			obj->name[i] = '.';
35 			dots++;
36 		}
37 
38 	if (obj->name_len <= 2 && dots == obj->name_len)
39 		obj->name[0] = '^';
40 
41 	obj->filetype = -1;
42 
43 	/*
44 	 * object is a file and is filetyped and timestamped?
45 	 * RISC OS 12-bit filetype is stored in load_address[19:8]
46 	 */
47 	if ((0 == (obj->attr & ADFS_NDA_DIRECTORY)) &&
48 	    (0xfff00000 == (0xfff00000 & obj->loadaddr))) {
49 		obj->filetype = (__u16) ((0x000fff00 & obj->loadaddr) >> 8);
50 
51 		/* optionally append the ,xyz hex filetype suffix */
52 		if (ADFS_SB(dir->sb)->s_ftsuffix) {
53 			__u16 filetype = obj->filetype;
54 
55 			obj->name[obj->name_len++] = ',';
56 			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 8);
57 			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 4);
58 			obj->name[obj->name_len++] = hex_asc_lo(filetype >> 0);
59 		}
60 	}
61 }
62 
63 static int
64 adfs_readdir(struct file *file, struct dir_context *ctx)
65 {
66 	struct inode *inode = file_inode(file);
67 	struct super_block *sb = inode->i_sb;
68 	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
69 	struct object_info obj;
70 	struct adfs_dir dir;
71 	int ret = 0;
72 
73 	if (ctx->pos >> 32)
74 		return 0;
75 
76 	ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
77 	if (ret)
78 		return ret;
79 
80 	if (ctx->pos == 0) {
81 		if (!dir_emit_dot(file, ctx))
82 			goto free_out;
83 		ctx->pos = 1;
84 	}
85 	if (ctx->pos == 1) {
86 		if (!dir_emit(ctx, "..", 2, dir.parent_id, DT_DIR))
87 			goto free_out;
88 		ctx->pos = 2;
89 	}
90 
91 	read_lock(&adfs_dir_lock);
92 
93 	ret = ops->setpos(&dir, ctx->pos - 2);
94 	if (ret)
95 		goto unlock_out;
96 	while (ops->getnext(&dir, &obj) == 0) {
97 		if (!dir_emit(ctx, obj.name, obj.name_len,
98 			    obj.file_id, DT_UNKNOWN))
99 			break;
100 		ctx->pos++;
101 	}
102 
103 unlock_out:
104 	read_unlock(&adfs_dir_lock);
105 
106 free_out:
107 	ops->free(&dir);
108 	return ret;
109 }
110 
111 int
112 adfs_dir_update(struct super_block *sb, struct object_info *obj, int wait)
113 {
114 	int ret = -EINVAL;
115 #ifdef CONFIG_ADFS_FS_RW
116 	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
117 	struct adfs_dir dir;
118 
119 	printk(KERN_INFO "adfs_dir_update: object %06X in dir %06X\n",
120 		 obj->file_id, obj->parent_id);
121 
122 	if (!ops->update) {
123 		ret = -EINVAL;
124 		goto out;
125 	}
126 
127 	ret = ops->read(sb, obj->parent_id, 0, &dir);
128 	if (ret)
129 		goto out;
130 
131 	write_lock(&adfs_dir_lock);
132 	ret = ops->update(&dir, obj);
133 	write_unlock(&adfs_dir_lock);
134 
135 	if (wait) {
136 		int err = ops->sync(&dir);
137 		if (!ret)
138 			ret = err;
139 	}
140 
141 	ops->free(&dir);
142 out:
143 #endif
144 	return ret;
145 }
146 
147 static unsigned char adfs_tolower(unsigned char c)
148 {
149 	if (c >= 'A' && c <= 'Z')
150 		c += 'a' - 'A';
151 	return c;
152 }
153 
154 static int __adfs_compare(const unsigned char *qstr, u32 qlen,
155 			  const char *str, u32 len)
156 {
157 	u32 i;
158 
159 	if (qlen != len)
160 		return 1;
161 
162 	for (i = 0; i < qlen; i++)
163 		if (adfs_tolower(qstr[i]) != adfs_tolower(str[i]))
164 			return 1;
165 
166 	return 0;
167 }
168 
169 static int adfs_dir_lookup_byname(struct inode *inode, const struct qstr *qstr,
170 				  struct object_info *obj)
171 {
172 	struct super_block *sb = inode->i_sb;
173 	const struct adfs_dir_ops *ops = ADFS_SB(sb)->s_dir;
174 	const unsigned char *name;
175 	struct adfs_dir dir;
176 	u32 name_len;
177 	int ret;
178 
179 	ret = ops->read(sb, inode->i_ino, inode->i_size, &dir);
180 	if (ret)
181 		goto out;
182 
183 	if (ADFS_I(inode)->parent_id != dir.parent_id) {
184 		adfs_error(sb, "parent directory changed under me! (%lx but got %x)\n",
185 			   ADFS_I(inode)->parent_id, dir.parent_id);
186 		ret = -EIO;
187 		goto free_out;
188 	}
189 
190 	obj->parent_id = inode->i_ino;
191 
192 	read_lock(&adfs_dir_lock);
193 
194 	ret = ops->setpos(&dir, 0);
195 	if (ret)
196 		goto unlock_out;
197 
198 	ret = -ENOENT;
199 	name = qstr->name;
200 	name_len = qstr->len;
201 	while (ops->getnext(&dir, obj) == 0) {
202 		if (!__adfs_compare(name, name_len, obj->name, obj->name_len)) {
203 			ret = 0;
204 			break;
205 		}
206 	}
207 
208 unlock_out:
209 	read_unlock(&adfs_dir_lock);
210 
211 free_out:
212 	ops->free(&dir);
213 out:
214 	return ret;
215 }
216 
217 const struct file_operations adfs_dir_operations = {
218 	.read		= generic_read_dir,
219 	.llseek		= generic_file_llseek,
220 	.iterate	= adfs_readdir,
221 	.fsync		= generic_file_fsync,
222 };
223 
224 static int
225 adfs_hash(const struct dentry *parent, struct qstr *qstr)
226 {
227 	const unsigned char *name;
228 	unsigned long hash;
229 	u32 len;
230 
231 	if (qstr->len > ADFS_SB(parent->d_sb)->s_namelen)
232 		return -ENAMETOOLONG;
233 
234 	len = qstr->len;
235 	name = qstr->name;
236 	hash = init_name_hash(parent);
237 	while (len--)
238 		hash = partial_name_hash(adfs_tolower(*name++), hash);
239 	qstr->hash = end_name_hash(hash);
240 
241 	return 0;
242 }
243 
244 /*
245  * Compare two names, taking note of the name length
246  * requirements of the underlying filesystem.
247  */
248 static int adfs_compare(const struct dentry *dentry, unsigned int len,
249 			const char *str, const struct qstr *qstr)
250 {
251 	return __adfs_compare(qstr->name, qstr->len, str, len);
252 }
253 
254 const struct dentry_operations adfs_dentry_operations = {
255 	.d_hash		= adfs_hash,
256 	.d_compare	= adfs_compare,
257 };
258 
259 static struct dentry *
260 adfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags)
261 {
262 	struct inode *inode = NULL;
263 	struct object_info obj;
264 	int error;
265 
266 	error = adfs_dir_lookup_byname(dir, &dentry->d_name, &obj);
267 	if (error == 0) {
268 		/*
269 		 * This only returns NULL if get_empty_inode
270 		 * fails.
271 		 */
272 		inode = adfs_iget(dir->i_sb, &obj);
273 		if (!inode)
274 			inode = ERR_PTR(-EACCES);
275 	} else if (error != -ENOENT) {
276 		inode = ERR_PTR(error);
277 	}
278 	return d_splice_alias(inode, dentry);
279 }
280 
281 /*
282  * directories can handle most operations...
283  */
284 const struct inode_operations adfs_dir_inode_operations = {
285 	.lookup		= adfs_lookup,
286 	.setattr	= adfs_notify_change,
287 };
288