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