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

TOMOYO Linux Cross Reference
Linux/fs/adfs/dir_f.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 /*
  2  *  linux/fs/adfs/dir_f.c
  3  *
  4  * Copyright (C) 1997-1999 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  *  E and F format directory handling
 11  */
 12 #include <linux/buffer_head.h>
 13 #include "adfs.h"
 14 #include "dir_f.h"
 15 
 16 static void adfs_f_free(struct adfs_dir *dir);
 17 
 18 /*
 19  * Read an (unaligned) value of length 1..4 bytes
 20  */
 21 static inline unsigned int adfs_readval(unsigned char *p, int len)
 22 {
 23         unsigned int val = 0;
 24 
 25         switch (len) {
 26         case 4:         val |= p[3] << 24;
 27         case 3:         val |= p[2] << 16;
 28         case 2:         val |= p[1] << 8;
 29         default:        val |= p[0];
 30         }
 31         return val;
 32 }
 33 
 34 static inline void adfs_writeval(unsigned char *p, int len, unsigned int val)
 35 {
 36         switch (len) {
 37         case 4:         p[3] = val >> 24;
 38         case 3:         p[2] = val >> 16;
 39         case 2:         p[1] = val >> 8;
 40         default:        p[0] = val;
 41         }
 42 }
 43 
 44 static inline int adfs_readname(char *buf, char *ptr, int maxlen)
 45 {
 46         char *old_buf = buf;
 47 
 48         while ((unsigned char)*ptr >= ' ' && maxlen--) {
 49                 if (*ptr == '/')
 50                         *buf++ = '.';
 51                 else
 52                         *buf++ = *ptr;
 53                 ptr++;
 54         }
 55         *buf = '\0';
 56 
 57         return buf - old_buf;
 58 }
 59 
 60 #define ror13(v) ((v >> 13) | (v << 19))
 61 
 62 #define dir_u8(idx)                             \
 63         ({ int _buf = idx >> blocksize_bits;    \
 64            int _off = idx - (_buf << blocksize_bits);\
 65           *(u8 *)(bh[_buf]->b_data + _off);     \
 66         })
 67 
 68 #define dir_u32(idx)                            \
 69         ({ int _buf = idx >> blocksize_bits;    \
 70            int _off = idx - (_buf << blocksize_bits);\
 71           *(__le32 *)(bh[_buf]->b_data + _off); \
 72         })
 73 
 74 #define bufoff(_bh,_idx)                        \
 75         ({ int _buf = _idx >> blocksize_bits;   \
 76            int _off = _idx - (_buf << blocksize_bits);\
 77           (u8 *)(_bh[_buf]->b_data + _off);     \
 78         })
 79 
 80 /*
 81  * There are some algorithms that are nice in
 82  * assembler, but a bitch in C...  This is one
 83  * of them.
 84  */
 85 static u8
 86 adfs_dir_checkbyte(const struct adfs_dir *dir)
 87 {
 88         struct buffer_head * const *bh = dir->bh;
 89         const int blocksize_bits = dir->sb->s_blocksize_bits;
 90         union { __le32 *ptr32; u8 *ptr8; } ptr, end;
 91         u32 dircheck = 0;
 92         int last = 5 - 26;
 93         int i = 0;
 94 
 95         /*
 96          * Accumulate each word up to the last whole
 97          * word of the last directory entry.  This
 98          * can spread across several buffer heads.
 99          */
100         do {
101                 last += 26;
102                 do {
103                         dircheck = le32_to_cpu(dir_u32(i)) ^ ror13(dircheck);
104 
105                         i += sizeof(u32);
106                 } while (i < (last & ~3));
107         } while (dir_u8(last) != 0);
108 
109         /*
110          * Accumulate the last few bytes.  These
111          * bytes will be within the same bh.
112          */
113         if (i != last) {
114                 ptr.ptr8 = bufoff(bh, i);
115                 end.ptr8 = ptr.ptr8 + last - i;
116 
117                 do {
118                         dircheck = *ptr.ptr8++ ^ ror13(dircheck);
119                 } while (ptr.ptr8 < end.ptr8);
120         }
121 
122         /*
123          * The directory tail is in the final bh
124          * Note that contary to the RISC OS PRMs,
125          * the first few bytes are NOT included
126          * in the check.  All bytes are in the
127          * same bh.
128          */
129         ptr.ptr8 = bufoff(bh, 2008);
130         end.ptr8 = ptr.ptr8 + 36;
131 
132         do {
133                 __le32 v = *ptr.ptr32++;
134                 dircheck = le32_to_cpu(v) ^ ror13(dircheck);
135         } while (ptr.ptr32 < end.ptr32);
136 
137         return (dircheck ^ (dircheck >> 8) ^ (dircheck >> 16) ^ (dircheck >> 24)) & 0xff;
138 }
139 
140 /*
141  * Read and check that a directory is valid
142  */
143 static int
144 adfs_dir_read(struct super_block *sb, unsigned long object_id,
145               unsigned int size, struct adfs_dir *dir)
146 {
147         const unsigned int blocksize_bits = sb->s_blocksize_bits;
148         int blk = 0;
149 
150         /*
151          * Directories which are not a multiple of 2048 bytes
152          * are considered bad v2 [3.6]
153          */
154         if (size & 2047)
155                 goto bad_dir;
156 
157         size >>= blocksize_bits;
158 
159         dir->nr_buffers = 0;
160         dir->sb = sb;
161 
162         for (blk = 0; blk < size; blk++) {
163                 int phys;
164 
165                 phys = __adfs_block_map(sb, object_id, blk);
166                 if (!phys) {
167                         adfs_error(sb, "dir object %lX has a hole at offset %d",
168                                    object_id, blk);
169                         goto release_buffers;
170                 }
171 
172                 dir->bh[blk] = sb_bread(sb, phys);
173                 if (!dir->bh[blk])
174                         goto release_buffers;
175         }
176 
177         memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead));
178         memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail));
179 
180         if (dir->dirhead.startmasseq != dir->dirtail.new.endmasseq ||
181             memcmp(&dir->dirhead.startname, &dir->dirtail.new.endname, 4))
182                 goto bad_dir;
183 
184         if (memcmp(&dir->dirhead.startname, "Nick", 4) &&
185             memcmp(&dir->dirhead.startname, "Hugo", 4))
186                 goto bad_dir;
187 
188         if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte)
189                 goto bad_dir;
190 
191         dir->nr_buffers = blk;
192 
193         return 0;
194 
195 bad_dir:
196         adfs_error(sb, "corrupted directory fragment %lX",
197                    object_id);
198 release_buffers:
199         for (blk -= 1; blk >= 0; blk -= 1)
200                 brelse(dir->bh[blk]);
201 
202         dir->sb = NULL;
203 
204         return -EIO;
205 }
206 
207 /*
208  * convert a disk-based directory entry to a Linux ADFS directory entry
209  */
210 static inline void
211 adfs_dir2obj(struct object_info *obj, struct adfs_direntry *de)
212 {
213         obj->name_len = adfs_readname(obj->name, de->dirobname, ADFS_F_NAME_LEN);
214         obj->file_id  = adfs_readval(de->dirinddiscadd, 3);
215         obj->loadaddr = adfs_readval(de->dirload, 4);
216         obj->execaddr = adfs_readval(de->direxec, 4);
217         obj->size     = adfs_readval(de->dirlen,  4);
218         obj->attr     = de->newdiratts;
219 }
220 
221 /*
222  * convert a Linux ADFS directory entry to a disk-based directory entry
223  */
224 static inline void
225 adfs_obj2dir(struct adfs_direntry *de, struct object_info *obj)
226 {
227         adfs_writeval(de->dirinddiscadd, 3, obj->file_id);
228         adfs_writeval(de->dirload, 4, obj->loadaddr);
229         adfs_writeval(de->direxec, 4, obj->execaddr);
230         adfs_writeval(de->dirlen,  4, obj->size);
231         de->newdiratts = obj->attr;
232 }
233 
234 /*
235  * get a directory entry.  Note that the caller is responsible
236  * for holding the relevant locks.
237  */
238 static int
239 __adfs_dir_get(struct adfs_dir *dir, int pos, struct object_info *obj)
240 {
241         struct super_block *sb = dir->sb;
242         struct adfs_direntry de;
243         int thissize, buffer, offset;
244 
245         buffer = pos >> sb->s_blocksize_bits;
246 
247         if (buffer > dir->nr_buffers)
248                 return -EINVAL;
249 
250         offset = pos & (sb->s_blocksize - 1);
251         thissize = sb->s_blocksize - offset;
252         if (thissize > 26)
253                 thissize = 26;
254 
255         memcpy(&de, dir->bh[buffer]->b_data + offset, thissize);
256         if (thissize != 26)
257                 memcpy(((char *)&de) + thissize, dir->bh[buffer + 1]->b_data,
258                        26 - thissize);
259 
260         if (!de.dirobname[0])
261                 return -ENOENT;
262 
263         adfs_dir2obj(obj, &de);
264 
265         return 0;
266 }
267 
268 static int
269 __adfs_dir_put(struct adfs_dir *dir, int pos, struct object_info *obj)
270 {
271         struct super_block *sb = dir->sb;
272         struct adfs_direntry de;
273         int thissize, buffer, offset;
274 
275         buffer = pos >> sb->s_blocksize_bits;
276 
277         if (buffer > dir->nr_buffers)
278                 return -EINVAL;
279 
280         offset = pos & (sb->s_blocksize - 1);
281         thissize = sb->s_blocksize - offset;
282         if (thissize > 26)
283                 thissize = 26;
284 
285         /*
286          * Get the entry in total
287          */
288         memcpy(&de, dir->bh[buffer]->b_data + offset, thissize);
289         if (thissize != 26)
290                 memcpy(((char *)&de) + thissize, dir->bh[buffer + 1]->b_data,
291                        26 - thissize);
292 
293         /*
294          * update it
295          */
296         adfs_obj2dir(&de, obj);
297 
298         /*
299          * Put the new entry back
300          */
301         memcpy(dir->bh[buffer]->b_data + offset, &de, thissize);
302         if (thissize != 26)
303                 memcpy(dir->bh[buffer + 1]->b_data, ((char *)&de) + thissize,
304                        26 - thissize);
305 
306         return 0;
307 }
308 
309 /*
310  * the caller is responsible for holding the necessary
311  * locks.
312  */
313 static int
314 adfs_dir_find_entry(struct adfs_dir *dir, unsigned long object_id)
315 {
316         int pos, ret;
317 
318         ret = -ENOENT;
319 
320         for (pos = 5; pos < ADFS_NUM_DIR_ENTRIES * 26 + 5; pos += 26) {
321                 struct object_info obj;
322 
323                 if (!__adfs_dir_get(dir, pos, &obj))
324                         break;
325 
326                 if (obj.file_id == object_id) {
327                         ret = pos;
328                         break;
329                 }
330         }
331 
332         return ret;
333 }
334 
335 static int
336 adfs_f_read(struct super_block *sb, unsigned int id, unsigned int sz, struct adfs_dir *dir)
337 {
338         int ret;
339 
340         if (sz != ADFS_NEWDIR_SIZE)
341                 return -EIO;
342 
343         ret = adfs_dir_read(sb, id, sz, dir);
344         if (ret)
345                 adfs_error(sb, "unable to read directory");
346         else
347                 dir->parent_id = adfs_readval(dir->dirtail.new.dirparent, 3);
348 
349         return ret;
350 }
351 
352 static int
353 adfs_f_setpos(struct adfs_dir *dir, unsigned int fpos)
354 {
355         if (fpos >= ADFS_NUM_DIR_ENTRIES)
356                 return -ENOENT;
357 
358         dir->pos = 5 + fpos * 26;
359         return 0;
360 }
361 
362 static int
363 adfs_f_getnext(struct adfs_dir *dir, struct object_info *obj)
364 {
365         unsigned int ret;
366 
367         ret = __adfs_dir_get(dir, dir->pos, obj);
368         if (ret == 0)
369                 dir->pos += 26;
370 
371         return ret;
372 }
373 
374 static int
375 adfs_f_update(struct adfs_dir *dir, struct object_info *obj)
376 {
377         struct super_block *sb = dir->sb;
378         int ret, i;
379 
380         ret = adfs_dir_find_entry(dir, obj->file_id);
381         if (ret < 0) {
382                 adfs_error(dir->sb, "unable to locate entry to update");
383                 goto out;
384         }
385 
386         __adfs_dir_put(dir, ret, obj);
387  
388         /*
389          * Increment directory sequence number
390          */
391         dir->bh[0]->b_data[0] += 1;
392         dir->bh[dir->nr_buffers - 1]->b_data[sb->s_blocksize - 6] += 1;
393 
394         ret = adfs_dir_checkbyte(dir);
395         /*
396          * Update directory check byte
397          */
398         dir->bh[dir->nr_buffers - 1]->b_data[sb->s_blocksize - 1] = ret;
399 
400 #if 1
401         {
402         const unsigned int blocksize_bits = sb->s_blocksize_bits;
403 
404         memcpy(&dir->dirhead, bufoff(dir->bh, 0), sizeof(dir->dirhead));
405         memcpy(&dir->dirtail, bufoff(dir->bh, 2007), sizeof(dir->dirtail));
406 
407         if (dir->dirhead.startmasseq != dir->dirtail.new.endmasseq ||
408             memcmp(&dir->dirhead.startname, &dir->dirtail.new.endname, 4))
409                 goto bad_dir;
410 
411         if (memcmp(&dir->dirhead.startname, "Nick", 4) &&
412             memcmp(&dir->dirhead.startname, "Hugo", 4))
413                 goto bad_dir;
414 
415         if (adfs_dir_checkbyte(dir) != dir->dirtail.new.dircheckbyte)
416                 goto bad_dir;
417         }
418 #endif
419         for (i = dir->nr_buffers - 1; i >= 0; i--)
420                 mark_buffer_dirty(dir->bh[i]);
421 
422         ret = 0;
423 out:
424         return ret;
425 #if 1
426 bad_dir:
427         adfs_error(dir->sb, "whoops!  I broke a directory!");
428         return -EIO;
429 #endif
430 }
431 
432 static int
433 adfs_f_sync(struct adfs_dir *dir)
434 {
435         int err = 0;
436         int i;
437 
438         for (i = dir->nr_buffers - 1; i >= 0; i--) {
439                 struct buffer_head *bh = dir->bh[i];
440                 sync_dirty_buffer(bh);
441                 if (buffer_req(bh) && !buffer_uptodate(bh))
442                         err = -EIO;
443         }
444 
445         return err;
446 }
447 
448 static void
449 adfs_f_free(struct adfs_dir *dir)
450 {
451         int i;
452 
453         for (i = dir->nr_buffers - 1; i >= 0; i--) {
454                 brelse(dir->bh[i]);
455                 dir->bh[i] = NULL;
456         }
457 
458         dir->nr_buffers = 0;
459         dir->sb = NULL;
460 }
461 
462 struct adfs_dir_ops adfs_f_dir_ops = {
463         .read           = adfs_f_read,
464         .setpos         = adfs_f_setpos,
465         .getnext        = adfs_f_getnext,
466         .update         = adfs_f_update,
467         .sync           = adfs_f_sync,
468         .free           = adfs_f_free
469 };
470 

~ [ 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