OSDN Git Service

3621a057d7d17fe26cd99dd61026eb8460803377
[android-x86/external-parted.git] / libparted / fs / fat / traverse.c
1 /*
2     libparted
3     Copyright (C) 1998-2000, 2005, 2007-2010 Free Software Foundation,
4     Inc.
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 as published by
8     the Free Software Foundation; either version 3 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <config.h>
21 #include "fat.h"
22 #include "traverse.h"
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27
28 #ifndef DISCOVER_ONLY
29
30 #define NO_CLUSTER -1
31
32 static char tmp_buffer [4096];
33
34 int
35 fat_traverse_entries_per_buffer (FatTraverseInfo* trav_info)
36 {
37         return trav_info->buffer_size / sizeof (FatDirEntry);
38 }
39
40 /* returns 1 if there are no more directory entries in the directory being
41  * traversed, 0 otherwise.
42  */
43 static int
44 is_last_buffer (FatTraverseInfo* trav_info) {
45         FatSpecific*    fs_info = FAT_SPECIFIC (trav_info->fs);
46
47         if (trav_info->is_legacy_root_dir)
48                 return 1;
49         else
50                 return fat_table_is_eof (fs_info->fat, trav_info->next_buffer);
51 }
52
53 static int
54 write_root_dir (FatTraverseInfo* trav_info)
55 {
56         FatSpecific*    fs_info = FAT_SPECIFIC (trav_info->fs);
57
58         if (!ped_geometry_write (trav_info->fs->geom, trav_info->dir_entries,
59                                  fs_info->root_dir_offset,
60                                  fs_info->root_dir_sector_count))
61                 return 0;
62         if (!ped_geometry_sync (trav_info->fs->geom))
63                 return 0;
64         trav_info->dirty = 0;
65         return 1;
66 }
67
68 static int
69 write_dir_cluster (FatTraverseInfo* trav_info)
70 {
71         if (!fat_write_sync_cluster (trav_info->fs,
72                                      (void*) trav_info->dir_entries,
73                                      trav_info->this_buffer))
74                 return 0;
75         trav_info->dirty = 0;
76         return 1;
77 }
78
79 static int
80 write_dir_buffer (FatTraverseInfo* trav_info)
81 {
82         if (trav_info->is_legacy_root_dir)
83                 return write_root_dir (trav_info);
84         else
85                 return write_dir_cluster (trav_info);
86 }
87
88 static int
89 read_next_dir_buffer (FatTraverseInfo* trav_info)
90 {
91         FatSpecific*    fs_info = FAT_SPECIFIC (trav_info->fs);
92
93         PED_ASSERT (!trav_info->is_legacy_root_dir, return 0);
94
95         trav_info->this_buffer = trav_info->next_buffer;
96
97         if (trav_info->this_buffer < 2
98             || trav_info->this_buffer >= fs_info->cluster_count + 2) {
99                 ped_exception_throw (
100                         PED_EXCEPTION_ERROR,
101                         PED_EXCEPTION_CANCEL,
102                         "Cluster %ld in directory %s is outside file system!",
103                         (long) trav_info->this_buffer,
104                         trav_info->dir_name);
105                 return 0;
106         }
107
108         trav_info->next_buffer
109                 = fat_table_get (fs_info->fat, trav_info->this_buffer);
110
111         return fat_read_cluster (trav_info->fs, (void *) trav_info->dir_entries,
112                                  trav_info->this_buffer);
113 }
114
115 /* FIXME: put into fat_dir_entry_* operations */
116 void
117 fat_traverse_mark_dirty (FatTraverseInfo* trav_info)
118 {
119         trav_info->dirty = 1;
120 }
121
122 FatTraverseInfo*
123 fat_traverse_begin (PedFileSystem* fs, FatCluster start_cluster,
124                     const char* dir_name)
125 {
126         FatSpecific*            fs_info = FAT_SPECIFIC (fs);
127         FatTraverseInfo*        trav_info;
128
129         trav_info = (FatTraverseInfo*) ped_malloc (sizeof (FatTraverseInfo));
130         if (!trav_info)
131                 goto error;
132
133         trav_info->dir_name = strdup (dir_name);
134         if (!trav_info->dir_name)
135                 goto error_free_trav_info;
136
137         trav_info->fs = fs;
138         trav_info->is_legacy_root_dir
139                 = (fs_info->fat_type == FAT_TYPE_FAT16) && (start_cluster == 0);
140         trav_info->dirty = 0;
141         trav_info->eof = 0;
142         trav_info->current_entry = -1;
143
144         if (trav_info->is_legacy_root_dir) {
145                 trav_info->buffer_size = 512 * fs_info->root_dir_sector_count;
146         } else {
147                 trav_info->next_buffer = start_cluster;
148                 trav_info->buffer_size = fs_info->cluster_size;
149         }
150
151         trav_info->dir_entries
152                 = (FatDirEntry*) ped_malloc (trav_info->buffer_size);
153         if (!trav_info->dir_entries)
154                 goto error_free_dir_name;
155
156         if (trav_info->is_legacy_root_dir) {
157                 if (!ped_geometry_read (fs->geom, trav_info->dir_entries,
158                                         fs_info->root_dir_offset,
159                                         fs_info->root_dir_sector_count))
160                         goto error_free_dir_entries;
161         } else {
162                 if (!read_next_dir_buffer (trav_info))
163                         goto error_free_dir_entries;
164         }
165
166         return trav_info;
167
168 error_free_dir_entries:
169         free (trav_info->dir_entries);
170 error_free_dir_name:
171         free (trav_info->dir_name);
172 error_free_trav_info:
173         free (trav_info);
174 error:
175         return NULL;
176 }
177
178 int
179 fat_traverse_complete (FatTraverseInfo* trav_info)
180 {
181         if (trav_info->dirty) {
182                 if (!write_dir_buffer (trav_info))
183                         return 0;
184         }
185         free (trav_info->dir_entries);
186         free (trav_info->dir_name);
187         free (trav_info);
188         return 1;
189 }
190
191 FatTraverseInfo*
192 fat_traverse_directory (FatTraverseInfo *trav_info, FatDirEntry* parent)
193 {
194         strcpy (tmp_buffer, trav_info->dir_name);
195         fat_dir_entry_get_name (parent,
196                                 tmp_buffer + strlen (trav_info->dir_name));
197         strcat (tmp_buffer, "\\");
198
199         return fat_traverse_begin (trav_info->fs,
200                         fat_dir_entry_get_first_cluster (parent, trav_info->fs),
201                         tmp_buffer);
202 }
203
204 FatDirEntry*
205 fat_traverse_next_dir_entry (FatTraverseInfo *trav_info)
206 {
207         if (trav_info->eof)
208                 return NULL;
209
210         trav_info->current_entry++;
211         if (trav_info->current_entry
212                         >= fat_traverse_entries_per_buffer (trav_info)) {
213                 if (trav_info->dirty) {
214                         if (!write_dir_buffer (trav_info))
215                                 return NULL;
216                 }
217
218                 trav_info->current_entry = 0;
219                 if (is_last_buffer (trav_info)) {
220                         trav_info->eof = 1;
221                         return NULL;
222                 }
223                 if (!read_next_dir_buffer (trav_info))
224                         return NULL;
225         }
226         return trav_info->dir_entries + trav_info->current_entry;
227 }
228
229 FatCluster
230 fat_dir_entry_get_first_cluster (FatDirEntry* dir_entry, PedFileSystem *fs)
231 {
232         FatSpecific*            fs_info = FAT_SPECIFIC (fs);
233
234         switch (fs_info->fat_type) {
235         case FAT_TYPE_FAT12:
236         case FAT_TYPE_FAT16:
237                 return PED_LE16_TO_CPU (dir_entry->first_cluster);
238
239         case FAT_TYPE_FAT32:
240                 return PED_LE16_TO_CPU (dir_entry->first_cluster_high)
241                                 * 65536L
242                           + PED_LE16_TO_CPU (dir_entry->first_cluster);
243         }
244
245         return 0;
246 }
247
248 void
249 fat_dir_entry_set_first_cluster (FatDirEntry* dir_entry, PedFileSystem* fs,
250                                  FatCluster cluster)
251 {
252         FatSpecific*            fs_info = FAT_SPECIFIC (fs);
253
254         switch (fs_info->fat_type) {
255                 case FAT_TYPE_FAT12:
256                 PED_ASSERT (0, (void) 0);
257                 break;
258
259                 case FAT_TYPE_FAT16:
260                 dir_entry->first_cluster = PED_CPU_TO_LE16 (cluster);
261                 break;
262
263                 case FAT_TYPE_FAT32:
264                 dir_entry->first_cluster
265                         = PED_CPU_TO_LE16 (cluster & 0xffff);
266                 dir_entry->first_cluster_high
267                         = PED_CPU_TO_LE16 (cluster / 0x10000);
268                 break;
269         }
270 }
271
272 uint32_t
273 fat_dir_entry_get_length (FatDirEntry* dir_entry)
274 {
275         return PED_LE32_TO_CPU (dir_entry->length);
276 }
277
278 int
279 fat_dir_entry_is_null_term (const FatDirEntry* dir_entry)
280 {
281         FatDirEntry     null_entry;
282
283         memset (&null_entry, 0, sizeof (null_entry));
284         return memcmp (&null_entry, dir_entry, sizeof (null_entry)) == 0;
285 }
286
287 int
288 fat_dir_entry_is_active (FatDirEntry* dir_entry)
289 {
290         if ((unsigned char) dir_entry->name[0] == DELETED_FLAG) return 0;
291         if ((unsigned char) dir_entry->name[0] == 0) return 0;
292         if ((unsigned char) dir_entry->name[0] == 0xF6) return 0;
293         return 1;
294 }
295
296 int
297 fat_dir_entry_is_file (FatDirEntry* dir_entry) {
298         if (dir_entry->attributes == VFAT_ATTR) return 0;
299         if (dir_entry->attributes & VOLUME_LABEL_ATTR) return 0;
300         if (!fat_dir_entry_is_active (dir_entry)) return 0;
301         if ((dir_entry->attributes & DIRECTORY_ATTR) == DIRECTORY_ATTR) return 0;
302         return 1;
303 }
304
305 int
306 fat_dir_entry_is_system_file (FatDirEntry* dir_entry)
307 {
308         if (!fat_dir_entry_is_file (dir_entry)) return 0;
309         return (dir_entry->attributes & SYSTEM_ATTR)
310                 || (dir_entry->attributes & HIDDEN_ATTR);
311 }
312
313 int
314 fat_dir_entry_is_directory (FatDirEntry* dir_entry)
315 {
316         if (dir_entry->attributes == VFAT_ATTR) return 0;
317         if (dir_entry->attributes & VOLUME_LABEL_ATTR) return 0;
318         if (!fat_dir_entry_is_active (dir_entry)) return 0;
319         return (dir_entry->attributes & DIRECTORY_ATTR) == DIRECTORY_ATTR;
320 }
321
322 int
323 fat_dir_entry_has_first_cluster (FatDirEntry* dir_entry, PedFileSystem* fs)
324 {
325         FatSpecific*    fs_info = FAT_SPECIFIC (fs);
326         FatCluster      first_cluster;
327
328         if (!fat_dir_entry_is_file (dir_entry)
329                 && !fat_dir_entry_is_directory (dir_entry))
330                 return 0;
331
332         first_cluster = fat_dir_entry_get_first_cluster (dir_entry, fs);
333         if (first_cluster == 0
334                 || fat_table_is_eof (fs_info->fat, first_cluster))
335                 return 0;
336
337         return 1;
338 }
339
340 /*
341     decrypts silly DOS names to FILENAME.EXT
342 */
343 void
344 fat_dir_entry_get_name (const FatDirEntry *dir_entry, char *result) {
345         size_t i;
346         const char *src;
347         const char *ext;
348
349         src = dir_entry->name;
350
351         for (i=0; i < sizeof dir_entry->name; i++) {
352                 if (src[i] == ' ' || src[i] == 0) break;
353                 *result++ = src[i];
354         }
355
356         ext = (const char *) dir_entry->extension;
357         if (ext[0] != ' ' && ext[0] != 0) {
358                 *result++ = '.';
359                 for (i=0; i < sizeof dir_entry->extension; i++) {
360                         if (ext[i] == ' ' || ext[i] == 0) break;
361                         *result++ = ext[i];
362                 }
363         }
364
365         *result = 0;
366 }
367
368 #endif /* !DISCOVER_ONLY */