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

TOMOYO Linux Cross Reference
Linux/fs/hfs/extent.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  *  linux/fs/hfs/extent.c
  3  *
  4  * Copyright (C) 1995-1997  Paul H. Hargrove
  5  * (C) 2003 Ardis Technologies <roman@ardistech.com>
  6  * This file may be distributed under the terms of the GNU General Public License.
  7  *
  8  * This file contains the functions related to the extents B-tree.
  9  */
 10 
 11 #include <linux/pagemap.h>
 12 
 13 #include "hfs_fs.h"
 14 #include "btree.h"
 15 
 16 /*================ File-local functions ================*/
 17 
 18 /*
 19  * build_key
 20  */
 21 static void hfs_ext_build_key(hfs_btree_key *key, u32 cnid, u16 block, u8 type)
 22 {
 23         key->key_len = 7;
 24         key->ext.FkType = type;
 25         key->ext.FNum = cpu_to_be32(cnid);
 26         key->ext.FABN = cpu_to_be16(block);
 27 }
 28 
 29 /*
 30  * hfs_ext_compare()
 31  *
 32  * Description:
 33  *   This is the comparison function used for the extents B-tree.  In
 34  *   comparing extent B-tree entries, the file id is the most
 35  *   significant field (compared as unsigned ints); the fork type is
 36  *   the second most significant field (compared as unsigned chars);
 37  *   and the allocation block number field is the least significant
 38  *   (compared as unsigned ints).
 39  * Input Variable(s):
 40  *   struct hfs_ext_key *key1: pointer to the first key to compare
 41  *   struct hfs_ext_key *key2: pointer to the second key to compare
 42  * Output Variable(s):
 43  *   NONE
 44  * Returns:
 45  *   int: negative if key1<key2, positive if key1>key2, and 0 if key1==key2
 46  * Preconditions:
 47  *   key1 and key2 point to "valid" (struct hfs_ext_key)s.
 48  * Postconditions:
 49  *   This function has no side-effects */
 50 int hfs_ext_keycmp(const btree_key *key1, const btree_key *key2)
 51 {
 52         __be32 fnum1, fnum2;
 53         __be16 block1, block2;
 54 
 55         fnum1 = key1->ext.FNum;
 56         fnum2 = key2->ext.FNum;
 57         if (fnum1 != fnum2)
 58                 return be32_to_cpu(fnum1) < be32_to_cpu(fnum2) ? -1 : 1;
 59         if (key1->ext.FkType != key2->ext.FkType)
 60                 return key1->ext.FkType < key2->ext.FkType ? -1 : 1;
 61 
 62         block1 = key1->ext.FABN;
 63         block2 = key2->ext.FABN;
 64         if (block1 == block2)
 65                 return 0;
 66         return be16_to_cpu(block1) < be16_to_cpu(block2) ? -1 : 1;
 67 }
 68 
 69 /*
 70  * hfs_ext_find_block
 71  *
 72  * Find a block within an extent record
 73  */
 74 static u16 hfs_ext_find_block(struct hfs_extent *ext, u16 off)
 75 {
 76         int i;
 77         u16 count;
 78 
 79         for (i = 0; i < 3; ext++, i++) {
 80                 count = be16_to_cpu(ext->count);
 81                 if (off < count)
 82                         return be16_to_cpu(ext->block) + off;
 83                 off -= count;
 84         }
 85         /* panic? */
 86         return 0;
 87 }
 88 
 89 static int hfs_ext_block_count(struct hfs_extent *ext)
 90 {
 91         int i;
 92         u16 count = 0;
 93 
 94         for (i = 0; i < 3; ext++, i++)
 95                 count += be16_to_cpu(ext->count);
 96         return count;
 97 }
 98 
 99 static u16 hfs_ext_lastblock(struct hfs_extent *ext)
100 {
101         int i;
102 
103         ext += 2;
104         for (i = 0; i < 2; ext--, i++)
105                 if (ext->count)
106                         break;
107         return be16_to_cpu(ext->block) + be16_to_cpu(ext->count);
108 }
109 
110 static int __hfs_ext_write_extent(struct inode *inode, struct hfs_find_data *fd)
111 {
112         int res;
113 
114         hfs_ext_build_key(fd->search_key, inode->i_ino, HFS_I(inode)->cached_start,
115                           HFS_IS_RSRC(inode) ?  HFS_FK_RSRC : HFS_FK_DATA);
116         res = hfs_brec_find(fd);
117         if (HFS_I(inode)->flags & HFS_FLG_EXT_NEW) {
118                 if (res != -ENOENT)
119                         return res;
120                 /* Fail early and avoid ENOSPC during the btree operation */
121                 res = hfs_bmap_reserve(fd->tree, fd->tree->depth + 1);
122                 if (res)
123                         return res;
124                 hfs_brec_insert(fd, HFS_I(inode)->cached_extents, sizeof(hfs_extent_rec));
125                 HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW);
126         } else {
127                 if (res)
128                         return res;
129                 hfs_bnode_write(fd->bnode, HFS_I(inode)->cached_extents, fd->entryoffset, fd->entrylength);
130                 HFS_I(inode)->flags &= ~HFS_FLG_EXT_DIRTY;
131         }
132         return 0;
133 }
134 
135 int hfs_ext_write_extent(struct inode *inode)
136 {
137         struct hfs_find_data fd;
138         int res = 0;
139 
140         if (HFS_I(inode)->flags & HFS_FLG_EXT_DIRTY) {
141                 res = hfs_find_init(HFS_SB(inode->i_sb)->ext_tree, &fd);
142                 if (res)
143                         return res;
144                 res = __hfs_ext_write_extent(inode, &fd);
145                 hfs_find_exit(&fd);
146         }
147         return res;
148 }
149 
150 static inline int __hfs_ext_read_extent(struct hfs_find_data *fd, struct hfs_extent *extent,
151                                         u32 cnid, u32 block, u8 type)
152 {
153         int res;
154 
155         hfs_ext_build_key(fd->search_key, cnid, block, type);
156         fd->key->ext.FNum = 0;
157         res = hfs_brec_find(fd);
158         if (res && res != -ENOENT)
159                 return res;
160         if (fd->key->ext.FNum != fd->search_key->ext.FNum ||
161             fd->key->ext.FkType != fd->search_key->ext.FkType)
162                 return -ENOENT;
163         if (fd->entrylength != sizeof(hfs_extent_rec))
164                 return -EIO;
165         hfs_bnode_read(fd->bnode, extent, fd->entryoffset, sizeof(hfs_extent_rec));
166         return 0;
167 }
168 
169 static inline int __hfs_ext_cache_extent(struct hfs_find_data *fd, struct inode *inode, u32 block)
170 {
171         int res;
172 
173         if (HFS_I(inode)->flags & HFS_FLG_EXT_DIRTY) {
174                 res = __hfs_ext_write_extent(inode, fd);
175                 if (res)
176                         return res;
177         }
178 
179         res = __hfs_ext_read_extent(fd, HFS_I(inode)->cached_extents, inode->i_ino,
180                                     block, HFS_IS_RSRC(inode) ? HFS_FK_RSRC : HFS_FK_DATA);
181         if (!res) {
182                 HFS_I(inode)->cached_start = be16_to_cpu(fd->key->ext.FABN);
183                 HFS_I(inode)->cached_blocks = hfs_ext_block_count(HFS_I(inode)->cached_extents);
184         } else {
185                 HFS_I(inode)->cached_start = HFS_I(inode)->cached_blocks = 0;
186                 HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW);
187         }
188         return res;
189 }
190 
191 static int hfs_ext_read_extent(struct inode *inode, u16 block)
192 {
193         struct hfs_find_data fd;
194         int res;
195 
196         if (block >= HFS_I(inode)->cached_start &&
197             block < HFS_I(inode)->cached_start + HFS_I(inode)->cached_blocks)
198                 return 0;
199 
200         res = hfs_find_init(HFS_SB(inode->i_sb)->ext_tree, &fd);
201         if (!res) {
202                 res = __hfs_ext_cache_extent(&fd, inode, block);
203                 hfs_find_exit(&fd);
204         }
205         return res;
206 }
207 
208 static void hfs_dump_extent(struct hfs_extent *extent)
209 {
210         int i;
211 
212         hfs_dbg(EXTENT, "   ");
213         for (i = 0; i < 3; i++)
214                 hfs_dbg_cont(EXTENT, " %u:%u",
215                              be16_to_cpu(extent[i].block),
216                              be16_to_cpu(extent[i].count));
217         hfs_dbg_cont(EXTENT, "\n");
218 }
219 
220 static int hfs_add_extent(struct hfs_extent *extent, u16 offset,
221                           u16 alloc_block, u16 block_count)
222 {
223         u16 count, start;
224         int i;
225 
226         hfs_dump_extent(extent);
227         for (i = 0; i < 3; extent++, i++) {
228                 count = be16_to_cpu(extent->count);
229                 if (offset == count) {
230                         start = be16_to_cpu(extent->block);
231                         if (alloc_block != start + count) {
232                                 if (++i >= 3)
233                                         return -ENOSPC;
234                                 extent++;
235                                 extent->block = cpu_to_be16(alloc_block);
236                         } else
237                                 block_count += count;
238                         extent->count = cpu_to_be16(block_count);
239                         return 0;
240                 } else if (offset < count)
241                         break;
242                 offset -= count;
243         }
244         /* panic? */
245         return -EIO;
246 }
247 
248 static int hfs_free_extents(struct super_block *sb, struct hfs_extent *extent,
249                             u16 offset, u16 block_nr)
250 {
251         u16 count, start;
252         int i;
253 
254         hfs_dump_extent(extent);
255         for (i = 0; i < 3; extent++, i++) {
256                 count = be16_to_cpu(extent->count);
257                 if (offset == count)
258                         goto found;
259                 else if (offset < count)
260                         break;
261                 offset -= count;
262         }
263         /* panic? */
264         return -EIO;
265 found:
266         for (;;) {
267                 start = be16_to_cpu(extent->block);
268                 if (count <= block_nr) {
269                         hfs_clear_vbm_bits(sb, start, count);
270                         extent->block = 0;
271                         extent->count = 0;
272                         block_nr -= count;
273                 } else {
274                         count -= block_nr;
275                         hfs_clear_vbm_bits(sb, start + count, block_nr);
276                         extent->count = cpu_to_be16(count);
277                         block_nr = 0;
278                 }
279                 if (!block_nr || !i)
280                         return 0;
281                 i--;
282                 extent--;
283                 count = be16_to_cpu(extent->count);
284         }
285 }
286 
287 int hfs_free_fork(struct super_block *sb, struct hfs_cat_file *file, int type)
288 {
289         struct hfs_find_data fd;
290         u32 total_blocks, blocks, start;
291         u32 cnid = be32_to_cpu(file->FlNum);
292         struct hfs_extent *extent;
293         int res, i;
294 
295         if (type == HFS_FK_DATA) {
296                 total_blocks = be32_to_cpu(file->PyLen);
297                 extent = file->ExtRec;
298         } else {
299                 total_blocks = be32_to_cpu(file->RPyLen);
300                 extent = file->RExtRec;
301         }
302         total_blocks /= HFS_SB(sb)->alloc_blksz;
303         if (!total_blocks)
304                 return 0;
305 
306         blocks = 0;
307         for (i = 0; i < 3; i++)
308                 blocks += be16_to_cpu(extent[i].count);
309 
310         res = hfs_free_extents(sb, extent, blocks, blocks);
311         if (res)
312                 return res;
313         if (total_blocks == blocks)
314                 return 0;
315 
316         res = hfs_find_init(HFS_SB(sb)->ext_tree, &fd);
317         if (res)
318                 return res;
319         do {
320                 res = __hfs_ext_read_extent(&fd, extent, cnid, total_blocks, type);
321                 if (res)
322                         break;
323                 start = be16_to_cpu(fd.key->ext.FABN);
324                 hfs_free_extents(sb, extent, total_blocks - start, total_blocks);
325                 hfs_brec_remove(&fd);
326                 total_blocks = start;
327         } while (total_blocks > blocks);
328         hfs_find_exit(&fd);
329 
330         return res;
331 }
332 
333 /*
334  * hfs_get_block
335  */
336 int hfs_get_block(struct inode *inode, sector_t block,
337                   struct buffer_head *bh_result, int create)
338 {
339         struct super_block *sb;
340         u16 dblock, ablock;
341         int res;
342 
343         sb = inode->i_sb;
344         /* Convert inode block to disk allocation block */
345         ablock = (u32)block / HFS_SB(sb)->fs_div;
346 
347         if (block >= HFS_I(inode)->fs_blocks) {
348                 if (!create)
349                         return 0;
350                 if (block > HFS_I(inode)->fs_blocks)
351                         return -EIO;
352                 if (ablock >= HFS_I(inode)->alloc_blocks) {
353                         res = hfs_extend_file(inode);
354                         if (res)
355                                 return res;
356                 }
357         } else
358                 create = 0;
359 
360         if (ablock < HFS_I(inode)->first_blocks) {
361                 dblock = hfs_ext_find_block(HFS_I(inode)->first_extents, ablock);
362                 goto done;
363         }
364 
365         mutex_lock(&HFS_I(inode)->extents_lock);
366         res = hfs_ext_read_extent(inode, ablock);
367         if (!res)
368                 dblock = hfs_ext_find_block(HFS_I(inode)->cached_extents,
369                                             ablock - HFS_I(inode)->cached_start);
370         else {
371                 mutex_unlock(&HFS_I(inode)->extents_lock);
372                 return -EIO;
373         }
374         mutex_unlock(&HFS_I(inode)->extents_lock);
375 
376 done:
377         map_bh(bh_result, sb, HFS_SB(sb)->fs_start +
378                dblock * HFS_SB(sb)->fs_div +
379                (u32)block % HFS_SB(sb)->fs_div);
380 
381         if (create) {
382                 set_buffer_new(bh_result);
383                 HFS_I(inode)->phys_size += sb->s_blocksize;
384                 HFS_I(inode)->fs_blocks++;
385                 inode_add_bytes(inode, sb->s_blocksize);
386                 mark_inode_dirty(inode);
387         }
388         return 0;
389 }
390 
391 int hfs_extend_file(struct inode *inode)
392 {
393         struct super_block *sb = inode->i_sb;
394         u32 start, len, goal;
395         int res;
396 
397         mutex_lock(&HFS_I(inode)->extents_lock);
398         if (HFS_I(inode)->alloc_blocks == HFS_I(inode)->first_blocks)
399                 goal = hfs_ext_lastblock(HFS_I(inode)->first_extents);
400         else {
401                 res = hfs_ext_read_extent(inode, HFS_I(inode)->alloc_blocks);
402                 if (res)
403                         goto out;
404                 goal = hfs_ext_lastblock(HFS_I(inode)->cached_extents);
405         }
406 
407         len = HFS_I(inode)->clump_blocks;
408         start = hfs_vbm_search_free(sb, goal, &len);
409         if (!len) {
410                 res = -ENOSPC;
411                 goto out;
412         }
413 
414         hfs_dbg(EXTENT, "extend %lu: %u,%u\n", inode->i_ino, start, len);
415         if (HFS_I(inode)->alloc_blocks == HFS_I(inode)->first_blocks) {
416                 if (!HFS_I(inode)->first_blocks) {
417                         hfs_dbg(EXTENT, "first extents\n");
418                         /* no extents yet */
419                         HFS_I(inode)->first_extents[0].block = cpu_to_be16(start);
420                         HFS_I(inode)->first_extents[0].count = cpu_to_be16(len);
421                         res = 0;
422                 } else {
423                         /* try to append to extents in inode */
424                         res = hfs_add_extent(HFS_I(inode)->first_extents,
425                                              HFS_I(inode)->alloc_blocks,
426                                              start, len);
427                         if (res == -ENOSPC)
428                                 goto insert_extent;
429                 }
430                 if (!res) {
431                         hfs_dump_extent(HFS_I(inode)->first_extents);
432                         HFS_I(inode)->first_blocks += len;
433                 }
434         } else {
435                 res = hfs_add_extent(HFS_I(inode)->cached_extents,
436                                      HFS_I(inode)->alloc_blocks -
437                                      HFS_I(inode)->cached_start,
438                                      start, len);
439                 if (!res) {
440                         hfs_dump_extent(HFS_I(inode)->cached_extents);
441                         HFS_I(inode)->flags |= HFS_FLG_EXT_DIRTY;
442                         HFS_I(inode)->cached_blocks += len;
443                 } else if (res == -ENOSPC)
444                         goto insert_extent;
445         }
446 out:
447         mutex_unlock(&HFS_I(inode)->extents_lock);
448         if (!res) {
449                 HFS_I(inode)->alloc_blocks += len;
450                 mark_inode_dirty(inode);
451                 if (inode->i_ino < HFS_FIRSTUSER_CNID)
452                         set_bit(HFS_FLG_ALT_MDB_DIRTY, &HFS_SB(sb)->flags);
453                 set_bit(HFS_FLG_MDB_DIRTY, &HFS_SB(sb)->flags);
454                 hfs_mark_mdb_dirty(sb);
455         }
456         return res;
457 
458 insert_extent:
459         hfs_dbg(EXTENT, "insert new extent\n");
460         res = hfs_ext_write_extent(inode);
461         if (res)
462                 goto out;
463 
464         memset(HFS_I(inode)->cached_extents, 0, sizeof(hfs_extent_rec));
465         HFS_I(inode)->cached_extents[0].block = cpu_to_be16(start);
466         HFS_I(inode)->cached_extents[0].count = cpu_to_be16(len);
467         hfs_dump_extent(HFS_I(inode)->cached_extents);
468         HFS_I(inode)->flags |= HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW;
469         HFS_I(inode)->cached_start = HFS_I(inode)->alloc_blocks;
470         HFS_I(inode)->cached_blocks = len;
471 
472         res = 0;
473         goto out;
474 }
475 
476 void hfs_file_truncate(struct inode *inode)
477 {
478         struct super_block *sb = inode->i_sb;
479         struct hfs_find_data fd;
480         u16 blk_cnt, alloc_cnt, start;
481         u32 size;
482         int res;
483 
484         hfs_dbg(INODE, "truncate: %lu, %Lu -> %Lu\n",
485                 inode->i_ino, (long long)HFS_I(inode)->phys_size,
486                 inode->i_size);
487         if (inode->i_size > HFS_I(inode)->phys_size) {
488                 struct address_space *mapping = inode->i_mapping;
489                 void *fsdata = NULL;
490                 struct page *page;
491 
492                 /* XXX: Can use generic_cont_expand? */
493                 size = inode->i_size - 1;
494                 res = hfs_write_begin(NULL, mapping, size + 1, 0, &page,
495                                 &fsdata);
496                 if (!res) {
497                         res = generic_write_end(NULL, mapping, size + 1, 0, 0,
498                                         page, fsdata);
499                 }
500                 if (res)
501                         inode->i_size = HFS_I(inode)->phys_size;
502                 return;
503         } else if (inode->i_size == HFS_I(inode)->phys_size)
504                 return;
505         size = inode->i_size + HFS_SB(sb)->alloc_blksz - 1;
506         blk_cnt = size / HFS_SB(sb)->alloc_blksz;
507         alloc_cnt = HFS_I(inode)->alloc_blocks;
508         if (blk_cnt == alloc_cnt)
509                 goto out;
510 
511         mutex_lock(&HFS_I(inode)->extents_lock);
512         res = hfs_find_init(HFS_SB(sb)->ext_tree, &fd);
513         if (res) {
514                 mutex_unlock(&HFS_I(inode)->extents_lock);
515                 /* XXX: We lack error handling of hfs_file_truncate() */
516                 return;
517         }
518         while (1) {
519                 if (alloc_cnt == HFS_I(inode)->first_blocks) {
520                         hfs_free_extents(sb, HFS_I(inode)->first_extents,
521                                          alloc_cnt, alloc_cnt - blk_cnt);
522                         hfs_dump_extent(HFS_I(inode)->first_extents);
523                         HFS_I(inode)->first_blocks = blk_cnt;
524                         break;
525                 }
526                 res = __hfs_ext_cache_extent(&fd, inode, alloc_cnt);
527                 if (res)
528                         break;
529                 start = HFS_I(inode)->cached_start;
530                 hfs_free_extents(sb, HFS_I(inode)->cached_extents,
531                                  alloc_cnt - start, alloc_cnt - blk_cnt);
532                 hfs_dump_extent(HFS_I(inode)->cached_extents);
533                 if (blk_cnt > start) {
534                         HFS_I(inode)->flags |= HFS_FLG_EXT_DIRTY;
535                         break;
536                 }
537                 alloc_cnt = start;
538                 HFS_I(inode)->cached_start = HFS_I(inode)->cached_blocks = 0;
539                 HFS_I(inode)->flags &= ~(HFS_FLG_EXT_DIRTY|HFS_FLG_EXT_NEW);
540                 hfs_brec_remove(&fd);
541         }
542         hfs_find_exit(&fd);
543         mutex_unlock(&HFS_I(inode)->extents_lock);
544 
545         HFS_I(inode)->alloc_blocks = blk_cnt;
546 out:
547         HFS_I(inode)->phys_size = inode->i_size;
548         HFS_I(inode)->fs_blocks = (inode->i_size + sb->s_blocksize - 1) >> sb->s_blocksize_bits;
549         inode_set_bytes(inode, HFS_I(inode)->fs_blocks << sb->s_blocksize_bits);
550         mark_inode_dirty(inode);
551 }
552 

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

kernel.org | git.kernel.org | LWN.net | Project Home | 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.

sflogo.php