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 */
34static 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
61static char*
62get_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
74static int
75is_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
94static void
95print_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
114static PedSector
115remainder_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*/
129static int
130flag_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*/
217static int
218flag_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
295static void
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*/
313int
314fat_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
340FatClusterFlag
341fat_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
348PedSector
349fat_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
364FatClusterFlag
365fat_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
387int
388fat_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