~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/fs/adfs/dir.c

Version: ~ [ linux-5.4.2 ] ~ [ linux-5.3.15 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.88 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.158 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.206 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.206 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.78 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp