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