OSDN Git Service

maint: update copyright year ranges to include 2011
[android-x86/external-parted.git] / libparted / fs / hfs / file_plus.c
1 /*
2     libparted - a library for manipulating disk partitions
3     Copyright (C) 2004-2005, 2007, 2009-2011 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 "advfs_plus.h"
37
38 #include "file_plus.h"
39
40 /* Open the data fork of a file with its first eight extents and its CNID */
41 /* CNID and ext_desc must be in disc order, sect_nb in CPU order */
42 /* return null on failure */
43 HfsPPrivateFile*
44 hfsplus_file_open (PedFileSystem *fs, HfsPNodeID CNID,
45                    HfsPExtDataRec ext_desc, PedSector sect_nb)
46 {
47         HfsPPrivateFile* file;
48
49         file = (HfsPPrivateFile*) ped_malloc (sizeof (HfsPPrivateFile));
50         if (!file) return NULL;
51
52         file->fs = fs;
53         file->sect_nb = sect_nb;
54         file->CNID = CNID;
55         memcpy(file->first, ext_desc, sizeof (HfsPExtDataRec));
56         file->start_cache = 0;
57
58         return file;
59 }
60
61 /* Close an HFS+ file */
62 void
63 hfsplus_file_close (HfsPPrivateFile* file)
64 {
65         free (file);
66 }
67
68 /* warning : only works on data forks */
69 static int
70 hfsplus_get_extent_containing (HfsPPrivateFile* file, unsigned int block,
71                                HfsPExtDataRec cache, uint32_t* ptr_start_cache)
72 {
73         uint8_t                 record[sizeof (HfsPExtentKey)
74                                        + sizeof (HfsPExtDataRec)];
75         HfsPExtentKey           search;
76         HfsPExtentKey*          ret_key = (HfsPExtentKey*) record;
77         HfsPExtDescriptor*      ret_cache = (HfsPExtDescriptor*)
78                                               (record + sizeof (HfsPExtentKey));
79         HfsPPrivateFSData*      priv_data = (HfsPPrivateFSData*)
80                                                 file->fs->type_specific;
81
82         search.key_length = PED_CPU_TO_BE16 (sizeof (HfsPExtentKey) - 2);
83         search.type = HFS_DATA_FORK;
84         search.pad = 0;
85         search.file_ID = file->CNID;
86         search.start = PED_CPU_TO_BE32 (block);
87
88         if (!hfsplus_btree_search (priv_data->extents_file,
89                                    (HfsPPrivateGenericKey*) &search,
90                                    record, sizeof (record), NULL))
91                 return 0;
92
93         if (ret_key->file_ID != search.file_ID || ret_key->type != search.type)
94                 return 0;
95
96         memcpy (cache, ret_cache, sizeof(HfsPExtDataRec));
97         *ptr_start_cache = PED_BE32_TO_CPU (ret_key->start);
98
99         return 1;
100 }
101
102 /* find a sub extent contained in the desired area */
103 /* and with the same starting point */
104 /* return 0 in sector_count on error, or the physical area */
105 /* on the volume corresponding to the logical area in the file */
106 static HfsPPrivateExtent
107 hfsplus_file_find_extent (HfsPPrivateFile* file, PedSector sector,
108                           unsigned int nb)
109 {
110         HfsPPrivateExtent ret = {0,0};
111         HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
112                                         file->fs->type_specific;
113         unsigned int    sect_by_block = PED_BE32_TO_CPU (
114                                             priv_data->vh->block_size)
115                                         / PED_SECTOR_SIZE_DEFAULT;
116         unsigned int    i, s, vol_block, size;
117         PedSector       sect_size;
118         unsigned int    block  = sector / sect_by_block;
119         unsigned int    offset = sector % sect_by_block;
120
121         /* in the 8 first extent */
122         for (s = 0, i = 0; i < HFSP_EXT_NB; i++) {
123                 if ((block >= s) && (block < s + PED_BE32_TO_CPU (
124                                                 file->first[i].block_count))) {
125                         vol_block = (block - s)
126                                     + PED_BE32_TO_CPU (file->first[i]
127                                                        .start_block);
128                         size = PED_BE32_TO_CPU (file->first[i].block_count)
129                                 + s - block;
130                         goto plus_sector_found;
131                 }
132                 s += PED_BE32_TO_CPU (file->first[i].block_count);
133         }
134
135         /* in the 8 cached extent */
136         if (file->start_cache && block >= file->start_cache)
137         for (s = file->start_cache, i = 0; i < HFSP_EXT_NB; i++) {
138                 if ((block >= s) && (block < s + PED_BE32_TO_CPU (
139                                                 file->cache[i].block_count))) {
140                         vol_block = (block - s)
141                                     + PED_BE32_TO_CPU (file->cache[i]
142                                                        .start_block);
143                         size = PED_BE32_TO_CPU (file->cache[i].block_count)
144                                 + s - block;
145                         goto plus_sector_found;
146                 }
147                 s += PED_BE32_TO_CPU (file->cache[i].block_count);
148         }
149
150         /* update cache */
151         if (!hfsplus_get_extent_containing (file, block, file->cache,
152                                             &(file->start_cache))) {
153                 ped_exception_throw (
154                         PED_EXCEPTION_WARNING,
155                         PED_EXCEPTION_CANCEL,
156                         _("Could not update the extent cache for HFS+ file "
157                           "with CNID %X."),
158                         PED_BE32_TO_CPU(file->CNID));
159                 return ret; /* ret == {0,0} */
160         }
161
162         /* ret == {0,0} */
163         PED_ASSERT(file->start_cache && block >= file->start_cache, return ret);
164
165         for (s = file->start_cache, i = 0; i < HFSP_EXT_NB; i++) {
166                 if ((block >= s) && (block < s + PED_BE32_TO_CPU (
167                                                 file->cache[i].block_count))) {
168                         vol_block = (block - s)
169                                     + PED_BE32_TO_CPU (file->cache[i]
170                                                        .start_block);
171                         size = PED_BE32_TO_CPU (file->cache[i].block_count)
172                                 + s - block;
173                         goto plus_sector_found;
174                 }
175                 s += PED_BE32_TO_CPU (file->cache[i].block_count);
176         }
177
178         return ret;
179
180 plus_sector_found:
181         sect_size = (PedSector) size * sect_by_block - offset;
182         ret.start_sector = vol_block * sect_by_block + offset;
183         ret.sector_count = (sect_size < nb) ? sect_size : nb;
184         return ret;
185 }
186
187 int
188 hfsplus_file_read(HfsPPrivateFile* file, void *buf, PedSector sector,
189                   unsigned int nb)
190 {
191         HfsPPrivateExtent phy_area;
192         HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
193                                         file->fs->type_specific;
194         char *b = buf;
195
196         if (sector+nb < sector /* detect overflow */
197             || sector+nb > file->sect_nb) /* out of file */ {
198                 ped_exception_throw (
199                         PED_EXCEPTION_ERROR,
200                         PED_EXCEPTION_CANCEL,
201                         _("Trying to read HFS+ file with CNID %X behind EOF."),
202                         PED_BE32_TO_CPU(file->CNID));
203                 return 0;
204         }
205
206         while (nb) {
207                 phy_area = hfsplus_file_find_extent(file, sector, nb);
208                 if (phy_area.sector_count == 0) {
209                         ped_exception_throw (
210                                 PED_EXCEPTION_ERROR,
211                                 PED_EXCEPTION_CANCEL,
212                                 _("Could not find sector %lli of HFS+ file "
213                                   "with CNID %X."),
214                                 sector, PED_BE32_TO_CPU(file->CNID));
215                         return 0;
216                 }
217                 if (!ped_geometry_read(priv_data->plus_geom, b,
218                                        phy_area.start_sector,
219                                        phy_area.sector_count))
220                         return 0;
221
222                 nb -= phy_area.sector_count; /* < nb anyway ... */
223                 sector += phy_area.sector_count;
224                 b += phy_area.sector_count * PED_SECTOR_SIZE_DEFAULT;
225         }
226
227         return 1;
228 }
229
230 int
231 hfsplus_file_write(HfsPPrivateFile* file, void *buf, PedSector sector,
232                   unsigned int nb)
233 {
234         HfsPPrivateExtent phy_area;
235         HfsPPrivateFSData* priv_data = (HfsPPrivateFSData*)
236                                         file->fs->type_specific;
237         char *b = buf;
238
239         if (sector+nb < sector /* detect overflow */
240             || sector+nb > file->sect_nb) /* out of file */ {
241                 ped_exception_throw (
242                         PED_EXCEPTION_ERROR,
243                         PED_EXCEPTION_CANCEL,
244                         _("Trying to write HFS+ file with CNID %X behind EOF."),
245                         PED_BE32_TO_CPU(file->CNID));
246                 return 0;
247         }
248
249         while (nb) {
250                 phy_area = hfsplus_file_find_extent(file, sector, nb);
251                 if (phy_area.sector_count == 0) {
252                         ped_exception_throw (
253                                 PED_EXCEPTION_ERROR,
254                                 PED_EXCEPTION_CANCEL,
255                                 _("Could not find sector %lli of HFS+ file "
256                                   "with CNID %X."),
257                                 sector, PED_BE32_TO_CPU(file->CNID));
258                         return 0;
259                 }
260                 if (!ped_geometry_write(priv_data->plus_geom, b,
261                                        phy_area.start_sector,
262                                        phy_area.sector_count))
263                         return 0;
264
265                 nb -= phy_area.sector_count; /* < nb anyway ... */
266                 sector += phy_area.sector_count;
267                 b += phy_area.sector_count * PED_SECTOR_SIZE_DEFAULT;
268         }
269
270         return 1;
271 }
272
273 #endif /* !DISCOVER_ONLY */