xref: /onnv-gate/usr/src/lib/libparted/common/libparted/fs/fat/count.c (revision 9663:ace9a2ac3683)
1 /*
2     libparted
3     Copyright (C) 1998, 1999, 2000, 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 #if 0
30 /* extremely ugly hack: stick everything that obviously isn't an unmovable file
31  * in here.  Note: DAT is a bit dubious.  Unfortunately, it's used by the
32  * registry, so it'll be all over the place :-(
33  */
34 static char*	movable_extensions[] = {
35 	"",
36 	"1ST",
37 	"AVI",
38 	"BAK", "BAT", "BMP",
39 	"CFG", "COM", "CSS",
40 	"DAT", "DLL", "DOC", "DRV",
41 	"EXE",
42 	"FAQ", "FLT", "FON",
43 	"GID", "GIF",
44 	"HLP", "HTT", "HTM",
45 	"ICO", "INI",
46 	"JPG",
47 	"LNK", "LOG",
48 	"KBD",
49 	"ME", "MID", "MSG",
50 	"OCX", "OLD",
51 	"PIF", "PNG", "PRV",
52 	"RTF",
53 	"SCR", "SYS",
54 	"TMP", "TTF", "TXT",
55 	"URL",
56 	"WAV",
57 	"VBX", "VOC", "VXD",
58 	NULL
59 };
60 
61 static char*
62 get_extension (char* file_name)
63 {
64 	char*		ext;
65 
66 	ext = strrchr (file_name, '.');
67 	if (!ext)
68 		return "";
69 	if (strchr (ext, '\\'))
70 		return "";
71 	return ext + 1;
72 }
73 
74 static int
75 is_movable_system_file (char* file_name)
76 {
77 	char*		ext = get_extension (file_name);
78 	int		i;
79 
80 	for (i = 0; movable_extensions [i]; i++) {
81 		if (strcasecmp (ext, movable_extensions [i]) == 0)
82 			return 1;
83 	}
84 
85 	return 0;
86 }
87 #endif /* 0 */
88 
89 /*
90     prints out the sequence of clusters for a given file chain, beginning
91     at start_cluster.
92 */
93 #ifdef PED_VERBOSE
94 static void
print_chain(PedFileSystem * fs,FatCluster start)95 print_chain (PedFileSystem* fs, FatCluster start)
96 {
97 	FatSpecific*	fs_info = FAT_SPECIFIC (fs);
98 	FatCluster	clst;
99 	int		this_row;
100 
101 	this_row = 0;
102 	for (clst = start; !fat_table_is_eof (fs_info->fat, clst);
103 	     clst = fat_table_get (fs_info->fat, clst)) {
104 		printf ("  %d", (int) clst);
105 		if (++this_row == 7) {
106                         putchar ('\n');
107 			this_row = 0;
108 		}
109 	}
110 	putchar ('\n');
111 }
112 #endif /* PED_VERBOSE */
113 
114 static PedSector
remainder_round_up(PedSector a,PedSector b)115 remainder_round_up (PedSector a, PedSector b)
116 {
117 	PedSector	result;
118 
119 	result = a % b;
120 	if (!result)
121 		result = b;
122 	return result;
123 }
124 
125 /*
126     traverse the FAT for a file/directory, marking each entry's flag
127     to "flag".
128 */
129 static int
flag_traverse_fat(PedFileSystem * fs,const char * chain_name,FatCluster start,FatClusterFlag flag,PedSector size)130 flag_traverse_fat (PedFileSystem* fs, const char* chain_name, FatCluster start,
131 		   FatClusterFlag flag, PedSector size)
132 {
133 	FatSpecific*	fs_info = FAT_SPECIFIC (fs);
134 	FatCluster	clst;
135 	FatCluster	prev_clst;
136 	int		last_cluster_usage;
137 	FatCluster	chain_length = 0;
138 
139 	if (fat_table_is_eof (fs_info->fat, start)) {
140 		if (ped_exception_throw (
141 			PED_EXCEPTION_ERROR,
142 			PED_EXCEPTION_IGNORE_CANCEL,
143 			_("Bad directory entry for %s: first cluster is the "
144 			  "end of file marker."),
145 			chain_name)
146 				!= PED_EXCEPTION_IGNORE)
147 			return 0;
148 	}
149 
150 	for (prev_clst = clst = start; !fat_table_is_eof (fs_info->fat, clst);
151 	     prev_clst = clst, clst = fat_table_get (fs_info->fat, clst)) {
152 		chain_length++;
153 		if (!clst) {
154 			ped_exception_throw (PED_EXCEPTION_FATAL,
155 				PED_EXCEPTION_CANCEL,
156 				_("Bad FAT: unterminated chain for %s.  You "
157 				  "should run dosfsck or scandisk."),
158 				chain_name);
159 			return 0;
160 		}
161 
162 		if (clst >= fs_info->fat->cluster_count + 2) {
163 			ped_exception_throw (PED_EXCEPTION_FATAL,
164 				PED_EXCEPTION_CANCEL,
165 				_("Bad FAT: cluster %d outside file system "
166 				  "in chain for %s.  You should run dosfsck "
167 				  "or scandisk."),
168 				(int) clst, chain_name);
169 			return 0;
170 		}
171 
172 		if (fs_info->cluster_info [clst].flag != FAT_FLAG_FREE ) {
173 			ped_exception_throw (PED_EXCEPTION_FATAL,
174 				PED_EXCEPTION_CANCEL,
175 				_("Bad FAT: cluster %d is cross-linked for "
176 				  "%s.  You should run dosfsck or scandisk."),
177 				(int) clst, chain_name);
178 			return 0;
179 		}
180 
181 		if (flag == FAT_FLAG_DIRECTORY)
182 			fs_info->total_dir_clusters++;
183 
184 		fs_info->cluster_info [clst].flag = flag;
185 		fs_info->cluster_info [clst].units_used = 0;	/* 0 == 64 */
186 	}
187 
188 	if (size
189 	    && chain_length
190 	    		!= ped_div_round_up (size, fs_info->cluster_sectors)) {
191 		if (ped_exception_throw (
192 			PED_EXCEPTION_ERROR,
193 			PED_EXCEPTION_IGNORE_CANCEL,
194 			_("%s is %dk, but it has %d clusters (%dk)."),
195 			chain_name,
196 			(int) size / 2,
197 			(int) chain_length,
198 			(int) chain_length * fs_info->cluster_sectors / 2)
199 				!= PED_EXCEPTION_IGNORE)
200 			return 0;
201 	}
202 
203 	last_cluster_usage
204 		= ped_div_round_up (64 * remainder_round_up (size,
205 						fs_info->cluster_sectors),
206 				fs_info->cluster_sectors);
207 
208 	fs_info->cluster_info [prev_clst].units_used = last_cluster_usage;
209 
210 	return 1;
211 }
212 
213 /*
214     recursively traverses a directory, flagging all clusters in the process.
215     It frees the traverse_info structure before returning.
216 */
217 static int
flag_traverse_dir(FatTraverseInfo * trav_info)218 flag_traverse_dir (FatTraverseInfo* trav_info) {
219 	PedFileSystem*		fs = trav_info->fs;
220 	FatDirEntry*		this_entry;
221 	FatTraverseInfo*	subdir_trav_info;
222 	char			file_name [512];
223 	char*			file_name_start;
224 	FatCluster		first_cluster;
225 	PedSector		size;
226 
227 	PED_ASSERT (trav_info != NULL, return 0);
228 
229 	strcpy (file_name, trav_info->dir_name);
230 	file_name_start = file_name + strlen (file_name);
231 
232 	while ( (this_entry = fat_traverse_next_dir_entry (trav_info)) ) {
233 		if (fat_dir_entry_is_null_term (this_entry))
234 			break;
235 		if (!fat_dir_entry_has_first_cluster (this_entry, fs))
236 			continue;
237 		if (this_entry->name [0] == '.')
238 			continue;	/* skip . and .. entries */
239 
240 		fat_dir_entry_get_name (this_entry, file_name_start);
241 		first_cluster = fat_dir_entry_get_first_cluster(this_entry, fs);
242 		size = ped_div_round_up (fat_dir_entry_get_length (this_entry),
243 					 512);
244 
245 #ifdef PED_VERBOSE
246 		printf ("%s: ", file_name);
247 		print_chain (fs, first_cluster);
248 #endif
249 
250 #if 0
251 		if (fat_dir_entry_is_system_file (this_entry)
252 		    && !is_movable_system_file (file_name)) {
253                         PedExceptionOption ex_status;
254 			ex_status = ped_exception_throw (
255 				PED_EXCEPTION_WARNING,
256 				PED_EXCEPTION_IGNORE_CANCEL,
257 				_("The file %s is marked as a system file.  "
258 				"This means moving it could cause some "
259 				"programs to stop working."),
260 				file_name);
261 
262 			switch (ex_status) {
263 				case PED_EXCEPTION_CANCEL:
264 					return 0;
265 
266 				case PED_EXCEPTION_UNHANDLED:
267 					ped_exception_catch ();
268 				case PED_EXCEPTION_IGNORE:
269 			}
270 		}
271 #endif /* 0 */
272 
273 		if (fat_dir_entry_is_directory (this_entry)) {
274 			if (!flag_traverse_fat (fs, file_name, first_cluster,
275 						FAT_FLAG_DIRECTORY, size))
276 				return 0;
277 
278 			subdir_trav_info = fat_traverse_directory (trav_info,
279 								   this_entry);
280 			if (!subdir_trav_info)
281 				return 0;
282 			if (!flag_traverse_dir (subdir_trav_info))
283 				return 0;
284 		} else if (fat_dir_entry_is_file (this_entry)) {
285 			if (!flag_traverse_fat (fs, file_name, first_cluster,
286 						FAT_FLAG_FILE, size))
287 				return 0;
288 		}
289 	}
290 
291 	fat_traverse_complete (trav_info);
292 	return 1;
293 }
294 
295 static void
_mark_bad_clusters(PedFileSystem * fs)296 _mark_bad_clusters (PedFileSystem* fs)
297 {
298 	FatSpecific*	fs_info = FAT_SPECIFIC (fs);
299 	FatCluster	cluster;
300 
301 	for (cluster = 2; cluster < fs_info->cluster_count + 2; cluster++) {
302 		if (fat_table_is_bad (fs_info->fat, cluster))
303 			fs_info->cluster_info [cluster].flag = FAT_FLAG_BAD;
304 	}
305 }
306 
307 /*
308     fills in cluster_info.  Each FAT entry (= cluster) is flagged as either
309     FAT_FLAG_FREE, FAT_FLAG_FILE or FAT_FLAG_DIRECTORY.
310 
311     Also, the fraction of each cluster (x/64) is recorded
312 */
313 int
fat_collect_cluster_info(PedFileSystem * fs)314 fat_collect_cluster_info (PedFileSystem* fs) {
315 	FatSpecific*		fs_info = FAT_SPECIFIC (fs);
316 	FatTraverseInfo*	trav_info;
317 
318 	/* set all clusters to unused as a default */
319 	memset (fs_info->cluster_info, 0, fs_info->fat->cluster_count + 2);
320 	fs_info->total_dir_clusters = 0;
321 
322 	if (fs_info->fat_type == FAT_TYPE_FAT32) {
323 		trav_info = fat_traverse_begin (fs, fs_info->root_cluster,
324 						"\\");
325 		if (!flag_traverse_dir (trav_info))
326 			return 0;
327 		if (!flag_traverse_fat (fs, "\\", fs_info->root_cluster,
328                                         FAT_FLAG_DIRECTORY, 0))
329 			return 0;
330 	} else {
331 		trav_info = fat_traverse_begin (fs, FAT_ROOT, "\\");
332 		if (!flag_traverse_dir (trav_info))
333 			return 0;
334 	}
335 
336 	_mark_bad_clusters (fs);
337 	return 1;
338 }
339 
340 FatClusterFlag
fat_get_cluster_flag(PedFileSystem * fs,FatCluster cluster)341 fat_get_cluster_flag (PedFileSystem* fs, FatCluster cluster)
342 {
343 	FatSpecific*		fs_info = FAT_SPECIFIC (fs);
344 
345 	return fs_info->cluster_info [cluster].flag;
346 }
347 
348 PedSector
fat_get_cluster_usage(PedFileSystem * fs,FatCluster cluster)349 fat_get_cluster_usage (PedFileSystem* fs, FatCluster cluster)
350 {
351 	FatSpecific*		fs_info = FAT_SPECIFIC (fs);
352 	int			fraction;
353 
354 	if (fs_info->cluster_info [cluster].flag == FAT_FLAG_FREE)
355 		return 0;
356 
357 	fraction = fs_info->cluster_info [cluster].units_used;
358 	if (fraction == 0)
359 		fraction = 64;
360 
361 	return fraction * fs_info->cluster_sectors / 64;
362 }
363 
364 FatClusterFlag
fat_get_fragment_flag(PedFileSystem * fs,FatFragment frag)365 fat_get_fragment_flag (PedFileSystem* fs, FatFragment frag)
366 {
367 	FatSpecific*	fs_info = FAT_SPECIFIC (fs);
368 	FatCluster	cluster = fat_frag_to_cluster (fs, frag);
369 	FatFragment	offset = frag % fs_info->cluster_frags;
370 	FatFragment	last_frag_used;
371 	FatClusterFlag	flag;
372 
373 	PED_ASSERT (cluster >= 2 && cluster < fs_info->cluster_count + 2,
374 		    return 0);
375 
376 	flag = fat_get_cluster_flag (fs, cluster);
377 	if (flag != FAT_FLAG_FILE && flag != FAT_FLAG_DIRECTORY)
378 		return flag;
379 	last_frag_used = (fat_get_cluster_usage (fs, cluster) - 1)
380 				/ fs_info->frag_sectors;
381 	if (offset > last_frag_used)
382 		return FAT_FLAG_FREE;
383 	else
384 		return flag;
385 }
386 
387 int
fat_is_fragment_active(PedFileSystem * fs,FatFragment frag)388 fat_is_fragment_active (PedFileSystem* fs, FatFragment frag)
389 {
390 	switch (fat_get_fragment_flag (fs, frag)) {
391 		case FAT_FLAG_FREE:
392 		case FAT_FLAG_BAD:
393 			return 0;
394 
395 		case FAT_FLAG_FILE:
396 		case FAT_FLAG_DIRECTORY:
397 			return 1;
398 	}
399 	return 0;
400 }
401 
402 #endif /* !DISCOVER_ONLY */
403 
404