OSDN Git Service

maint: update copyrights in r/
[android-x86/external-parted.git] / libparted / fs / r / hfs / advfs.c
1 /*
2     libparted - a library for manipulating disk partitions
3     Copyright (C) 2004-2005, 2007, 2009-2012 Free Software Foundation, Inc.
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 3 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #ifndef DISCOVER_ONLY
20
21 #include <config.h>
22
23 #include <parted/parted.h>
24 #include <parted/endian.h>
25 #include <parted/debug.h>
26 #include <stdint.h>
27
28 #if ENABLE_NLS
29 #  include <libintl.h>
30 #  define _(String) dgettext (PACKAGE, String)
31 #else
32 #  define _(String) (String)
33 #endif /* ENABLE_NLS */
34
35 #include "hfs.h"
36 #include "file.h"
37
38 #include "advfs.h"
39
40 /* - if a < b, 0 if a == b, + if a > b */
41 /* Comparaison is done in the following order : */
42 /* CNID, then fork type, then start block */
43 /* Note that HFS implementation in linux has a bug */
44 /* in this function */
45 static int
46 hfs_extent_key_cmp(HfsPrivateGenericKey* a, HfsPrivateGenericKey* b)
47 {
48         HfsExtentKey* key1 = (HfsExtentKey*) a;
49         HfsExtentKey* key2 = (HfsExtentKey*) b;
50
51         /* do NOT use a substraction, because */
52         /* 0xFFFFFFFF - 1 = 0xFFFFFFFE so this */
53         /* would return -2, despite the fact */
54         /* 0xFFFFFFFF > 1 !!! (this is the 2.4 bug) */
55         if (key1->file_ID != key2->file_ID)
56                 return PED_BE32_TO_CPU(key1->file_ID) <
57                        PED_BE32_TO_CPU(key2->file_ID) ?
58                                 -1 : +1;
59
60         if (key1->type != key2->type)
61                 return (int)(key1->type - key2->type);
62
63         if (key1->start == key2->start)
64                 return 0;
65         /* the whole thing wont work with 16 bits ints */
66         /* anyway */
67         return (int)( PED_BE16_TO_CPU(key1->start) -
68                       PED_BE16_TO_CPU(key2->start) );
69 }
70
71 /* do a B-Tree lookup */
72 /* read the first record immediatly inferior or egal to the given key */
73 /* return 0 on error */
74 /* record_out _must_ be large enough to receive record_size bytes */
75 /* WARNING : the compare function called only handle Extents BTree */
76 /*           so modify this function if you want to do lookup in */
77 /*           other BTrees has well */
78 int
79 hfs_btree_search (HfsPrivateFile* b_tree_file, HfsPrivateGenericKey* key,
80                   void *record_out, unsigned int record_size,
81                   HfsCPrivateLeafRec* record_ref)
82 {
83         uint8_t                 node[PED_SECTOR_SIZE_DEFAULT];
84         HfsHeaderRecord*        header;
85         HfsNodeDescriptor*      desc = (HfsNodeDescriptor*) node;
86         HfsPrivateGenericKey*   record_key = NULL;
87         unsigned int            node_number, record_number;
88         int                     i;
89
90         /* Read the header node */
91         if (!hfs_file_read_sector(b_tree_file, node, 0))
92                 return 0;
93         header = ((HfsHeaderRecord*) (node + PED_BE16_TO_CPU(*((uint16_t *)
94                                                 (node+(PED_SECTOR_SIZE_DEFAULT-2))))));
95
96         /* Get the node number of the root */
97         node_number = PED_BE32_TO_CPU(header->root_node);
98         if (!node_number)
99                 return 0;
100
101         /* Read the root node */
102         if (!hfs_file_read_sector(b_tree_file, node, node_number))
103                 return 0;
104
105         /* Follow the white rabbit */
106         while (1) {
107                 record_number = PED_BE16_TO_CPU (desc->rec_nb);
108                 for (i = record_number; i; i--) {
109                         record_key = (HfsPrivateGenericKey*)
110                                 (node + PED_BE16_TO_CPU(*((uint16_t *)
111                                             (node+(PED_SECTOR_SIZE_DEFAULT - 2*i)))));
112                         /* check for obvious error in FS */
113                         if (((uint8_t*)record_key - node < HFS_FIRST_REC)
114                             || ((uint8_t*)record_key - node
115                                 >= PED_SECTOR_SIZE_DEFAULT
116                                    - 2 * (signed)(record_number+1))) {
117                                 ped_exception_throw (
118                                         PED_EXCEPTION_ERROR,
119                                         PED_EXCEPTION_CANCEL,
120                                         _("The file system contains errors."));
121                                 return 0;
122                         }
123                         if (hfs_extent_key_cmp(record_key, key) <= 0)
124                                 break;
125                 }
126                 if (!i) return 0;
127                 if (desc->type == HFS_IDX_NODE) {
128                         unsigned int    skip;
129
130                         skip = (1 + record_key->key_length + 1) & ~1;
131                         node_number = PED_BE32_TO_CPU (*((uint32_t *)
132                                         (((uint8_t *) record_key) + skip)));
133                         if (!hfs_file_read_sector(b_tree_file, node,
134                                                   node_number))
135                                 return 0;
136                 } else
137                         break;
138         }
139
140         /* copy the result if needed */
141         if (record_size)
142                 memcpy (record_out, record_key, record_size);
143
144         /* send record reference if needed */
145         if (record_ref) {
146                 record_ref->node_size = 1;      /* in sectors */
147                 record_ref->node_number = node_number;
148                 record_ref->record_pos = (uint8_t*)record_key - node;
149                 record_ref->record_number = i;
150         }
151
152         /* success */
153         return 1;
154 }
155
156 /* free the bad blocks linked list */
157 void
158 hfs_free_bad_blocks_list(HfsPrivateLinkExtent* first)
159 {
160         HfsPrivateLinkExtent*   next;
161
162         while (first) {
163                 next = first->next;
164                 free (first);
165                 first = next;
166         }
167 }
168
169 /* This function reads bad blocks extents in the extents file
170    and store it in f.s. specific data of fs */
171 int
172 hfs_read_bad_blocks (const PedFileSystem *fs)
173 {
174         HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
175                                                 fs->type_specific;
176
177         if (priv_data->bad_blocks_loaded)
178                 return 1;
179
180         {
181         uint8_t                 record[sizeof (HfsExtentKey)
182                                                + sizeof (HfsExtDataRec)];
183         HfsExtentKey            search;
184         HfsExtentKey*           ret_key = (HfsExtentKey*) record;
185         HfsExtDescriptor*       ret_cache = (HfsExtDescriptor*)
186                                              (record + sizeof (HfsExtentKey));
187         unsigned int            block, last_start, first_pass = 1;
188
189         search.key_length = sizeof (HfsExtentKey) - 1;
190         search.type = HFS_DATA_FORK;
191         search.file_ID = PED_CPU_TO_BE32 (HFS_BAD_BLOCK_ID);
192
193         last_start = -1; block = 0;
194         while (1) {
195                 int i;
196
197                 search.start = PED_CPU_TO_BE16 (block);
198                 if (!hfs_btree_search (priv_data->extent_file,
199                                        (HfsPrivateGenericKey*) &search,
200                                        record, sizeof (record), NULL)
201                     || ret_key->file_ID != search.file_ID
202                     || ret_key->type != search.type) {
203                         if (first_pass)
204                                 break;
205                         else
206                                 goto errbb;
207                 }
208                 if (PED_BE16_TO_CPU (ret_key->start) == last_start)
209                     break;
210
211                 last_start = PED_BE16_TO_CPU (ret_key->start);
212                 for (i = 0; i < HFS_EXT_NB; i++) {
213                         if (ret_cache[i].block_count) {
214                                 HfsPrivateLinkExtent*   new_xt =
215                                    (HfsPrivateLinkExtent*) ped_malloc (
216                                         sizeof (HfsPrivateLinkExtent));
217                                 if (!new_xt)
218                                         goto errbb;
219                                 new_xt->next = priv_data->bad_blocks_xtent_list;
220                                 memcpy(&(new_xt->extent), ret_cache+i,
221                                         sizeof (HfsExtDescriptor));
222                                 priv_data->bad_blocks_xtent_list = new_xt;
223                                 priv_data->bad_blocks_xtent_nb++;
224                                 block += PED_BE16_TO_CPU (
225                                                 ret_cache[i].block_count);
226                         }
227                 }
228                 first_pass = 0;
229         }
230
231         priv_data->bad_blocks_loaded = 1;
232         return 1;}
233
234 errbb:  hfs_free_bad_blocks_list(priv_data->bad_blocks_xtent_list);
235         priv_data->bad_blocks_xtent_list=NULL;
236         priv_data->bad_blocks_xtent_nb=0;
237         return 0;
238 }
239
240 /* This function check if fblock is a bad block */
241 int
242 hfs_is_bad_block (const PedFileSystem *fs, unsigned int fblock)
243 {
244         HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
245                                                 fs->type_specific;
246         HfsPrivateLinkExtent*   walk;
247
248         for (walk = priv_data->bad_blocks_xtent_list; walk; walk = walk->next) {
249                 /* Won't compile without the strange cast ! gcc bug ? */
250                 /* or maybe C subtilties... */
251                 if ((fblock >= PED_BE16_TO_CPU (walk->extent.start_block)) &&
252                     (fblock <  (unsigned int) (PED_BE16_TO_CPU (
253                                                     walk->extent.start_block)
254                                                + PED_BE16_TO_CPU (
255                                                     walk->extent.block_count))))
256                         return 1;
257         }
258
259         return 0;
260 }
261
262 /* This function returns the first sector of the last free block of an
263    HFS volume we can get after a hfs_pack_free_space_from_block call */
264 /* On error this function returns 0 */
265 PedSector
266 hfs_get_empty_end (const PedFileSystem *fs)
267 {
268         HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
269                                                 fs->type_specific;
270         HfsMasterDirectoryBlock* mdb = priv_data->mdb;
271         unsigned int            block, last_bad, end_free_blocks;
272
273         /* find the next block to the last bad block of the volume */
274         if (!hfs_read_bad_blocks (fs))
275                 return 0;
276
277         HfsPrivateLinkExtent*   l;
278         last_bad = 0;
279         for (l = priv_data->bad_blocks_xtent_list; l; l = l->next) {
280                 if ((unsigned int) PED_BE16_TO_CPU (l->extent.start_block)
281                     + PED_BE16_TO_CPU (l->extent.block_count) > last_bad)
282                         last_bad = PED_BE16_TO_CPU (l->extent.start_block)
283                                    + PED_BE16_TO_CPU (l->extent.block_count);
284         }
285
286         /* Count the free blocks from last_bad to the end of the volume */
287         end_free_blocks = 0;
288         for (block = last_bad;
289              block < PED_BE16_TO_CPU (mdb->total_blocks);
290              block++) {
291                 if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
292                         end_free_blocks++;
293         }
294
295         /* Calculate the block that will by the first free at the
296            end of the volume */
297         block = PED_BE16_TO_CPU (mdb->total_blocks) - end_free_blocks;
298
299         return (PedSector) PED_BE16_TO_CPU (mdb->start_block)
300                 + (PedSector) block * (PED_BE32_TO_CPU (mdb->block_size)
301                                        / PED_SECTOR_SIZE_DEFAULT);
302 }
303
304 /* return the block which should be used to pack data to have at
305    least free fblock blocks at the end of the volume */
306 unsigned int
307 hfs_find_start_pack (const PedFileSystem *fs, unsigned int fblock)
308 {
309         HfsPrivateFSData*       priv_data = (HfsPrivateFSData*)
310                                                 fs->type_specific;
311         unsigned int            block;
312
313         for (block = PED_BE16_TO_CPU (priv_data->mdb->total_blocks) - 1;
314              block && fblock;
315              block--) {
316                 if (!TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
317                         fblock--;
318         }
319
320         while (block && !TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
321                 block--;
322         if (TST_BLOC_OCCUPATION(priv_data->alloc_map,block))
323                 block++;
324
325         return block;
326 }
327
328 #endif /* !DISCOVER_ONLY */