xref: /aosp_15_r20/external/exfatprogs/fsck/fsck.c (revision 508ec739de867a7549a0b8584942a00612dc5f1c)
1*508ec739SDaniel Rosenberg // SPDX-License-Identifier: GPL-2.0-or-later
2*508ec739SDaniel Rosenberg /*
3*508ec739SDaniel Rosenberg  *   Copyright (C) 2019 Namjae Jeon <[email protected]>
4*508ec739SDaniel Rosenberg  *   Copyright (C) 2020 Hyunchul Lee <[email protected]>
5*508ec739SDaniel Rosenberg  */
6*508ec739SDaniel Rosenberg 
7*508ec739SDaniel Rosenberg #include <unistd.h>
8*508ec739SDaniel Rosenberg #include <stdlib.h>
9*508ec739SDaniel Rosenberg #include <stdio.h>
10*508ec739SDaniel Rosenberg #include <getopt.h>
11*508ec739SDaniel Rosenberg #include <inttypes.h>
12*508ec739SDaniel Rosenberg #include <string.h>
13*508ec739SDaniel Rosenberg #include <errno.h>
14*508ec739SDaniel Rosenberg #include <locale.h>
15*508ec739SDaniel Rosenberg 
16*508ec739SDaniel Rosenberg #include "exfat_ondisk.h"
17*508ec739SDaniel Rosenberg #include "libexfat.h"
18*508ec739SDaniel Rosenberg #include "repair.h"
19*508ec739SDaniel Rosenberg #include "exfat_fs.h"
20*508ec739SDaniel Rosenberg #include "exfat_dir.h"
21*508ec739SDaniel Rosenberg #include "fsck.h"
22*508ec739SDaniel Rosenberg 
23*508ec739SDaniel Rosenberg struct fsck_user_input {
24*508ec739SDaniel Rosenberg 	struct exfat_user_input		ei;
25*508ec739SDaniel Rosenberg 	enum fsck_ui_options		options;
26*508ec739SDaniel Rosenberg };
27*508ec739SDaniel Rosenberg 
28*508ec739SDaniel Rosenberg #define EXFAT_MAX_UPCASE_CHARS	0x10000
29*508ec739SDaniel Rosenberg 
30*508ec739SDaniel Rosenberg #define FSCK_EXIT_NO_ERRORS		0x00
31*508ec739SDaniel Rosenberg #define FSCK_EXIT_CORRECTED		0x01
32*508ec739SDaniel Rosenberg #define FSCK_EXIT_NEED_REBOOT		0x02
33*508ec739SDaniel Rosenberg #define FSCK_EXIT_ERRORS_LEFT		0x04
34*508ec739SDaniel Rosenberg #define FSCK_EXIT_OPERATION_ERROR	0x08
35*508ec739SDaniel Rosenberg #define FSCK_EXIT_SYNTAX_ERROR		0x10
36*508ec739SDaniel Rosenberg #define FSCK_EXIT_USER_CANCEL		0x20
37*508ec739SDaniel Rosenberg #define FSCK_EXIT_LIBRARY_ERROR		0x80
38*508ec739SDaniel Rosenberg 
39*508ec739SDaniel Rosenberg struct exfat_stat {
40*508ec739SDaniel Rosenberg 	long		dir_count;
41*508ec739SDaniel Rosenberg 	long		file_count;
42*508ec739SDaniel Rosenberg 	long		error_count;
43*508ec739SDaniel Rosenberg 	long		fixed_count;
44*508ec739SDaniel Rosenberg };
45*508ec739SDaniel Rosenberg 
46*508ec739SDaniel Rosenberg struct exfat_fsck exfat_fsck;
47*508ec739SDaniel Rosenberg struct exfat_stat exfat_stat;
48*508ec739SDaniel Rosenberg struct path_resolve_ctx path_resolve_ctx;
49*508ec739SDaniel Rosenberg 
50*508ec739SDaniel Rosenberg static struct option opts[] = {
51*508ec739SDaniel Rosenberg 	{"repair",	no_argument,	NULL,	'r' },
52*508ec739SDaniel Rosenberg 	{"repair-yes",	no_argument,	NULL,	'y' },
53*508ec739SDaniel Rosenberg 	{"repair-no",	no_argument,	NULL,	'n' },
54*508ec739SDaniel Rosenberg 	{"repair-auto",	no_argument,	NULL,	'p' },
55*508ec739SDaniel Rosenberg 	{"rescue",	no_argument,	NULL,	's' },
56*508ec739SDaniel Rosenberg 	{"version",	no_argument,	NULL,	'V' },
57*508ec739SDaniel Rosenberg 	{"verbose",	no_argument,	NULL,	'v' },
58*508ec739SDaniel Rosenberg 	{"help",	no_argument,	NULL,	'h' },
59*508ec739SDaniel Rosenberg 	{"?",		no_argument,	NULL,	'?' },
60*508ec739SDaniel Rosenberg 	{"ignore-bad-fs",	no_argument,	NULL,	'b' },
61*508ec739SDaniel Rosenberg 	{NULL,		0,		NULL,	 0  }
62*508ec739SDaniel Rosenberg };
63*508ec739SDaniel Rosenberg 
usage(char * name)64*508ec739SDaniel Rosenberg static void usage(char *name)
65*508ec739SDaniel Rosenberg {
66*508ec739SDaniel Rosenberg 	fprintf(stderr, "Usage: %s\n", name);
67*508ec739SDaniel Rosenberg 	fprintf(stderr, "\t-r | --repair        Repair interactively\n");
68*508ec739SDaniel Rosenberg 	fprintf(stderr, "\t-y | --repair-yes    Repair without ask\n");
69*508ec739SDaniel Rosenberg 	fprintf(stderr, "\t-n | --repair-no     No repair\n");
70*508ec739SDaniel Rosenberg 	fprintf(stderr, "\t-p | --repair-auto   Repair automatically\n");
71*508ec739SDaniel Rosenberg 	fprintf(stderr, "\t-a                   Repair automatically\n");
72*508ec739SDaniel Rosenberg 	fprintf(stderr, "\t-b | --ignore-bad-fs Try to recover even if exfat is not found\n");
73*508ec739SDaniel Rosenberg 	fprintf(stderr, "\t-s | --rescue        Assign orphaned clusters to files\n");
74*508ec739SDaniel Rosenberg 	fprintf(stderr, "\t-V | --version       Show version\n");
75*508ec739SDaniel Rosenberg 	fprintf(stderr, "\t-v | --verbose       Print debug\n");
76*508ec739SDaniel Rosenberg 	fprintf(stderr, "\t-h | --help          Show help\n");
77*508ec739SDaniel Rosenberg 
78*508ec739SDaniel Rosenberg 	exit(FSCK_EXIT_SYNTAX_ERROR);
79*508ec739SDaniel Rosenberg }
80*508ec739SDaniel Rosenberg 
81*508ec739SDaniel Rosenberg #define fsck_err(parent, inode, fmt, ...)		\
82*508ec739SDaniel Rosenberg ({							\
83*508ec739SDaniel Rosenberg 		exfat_resolve_path_parent(&path_resolve_ctx,	\
84*508ec739SDaniel Rosenberg 			parent, inode);			\
85*508ec739SDaniel Rosenberg 		exfat_err("ERROR: %s: " fmt,		\
86*508ec739SDaniel Rosenberg 			path_resolve_ctx.local_path,	\
87*508ec739SDaniel Rosenberg 			##__VA_ARGS__);			\
88*508ec739SDaniel Rosenberg })
89*508ec739SDaniel Rosenberg 
90*508ec739SDaniel Rosenberg #define repair_file_ask(iter, inode, code, fmt, ...)	\
91*508ec739SDaniel Rosenberg ({							\
92*508ec739SDaniel Rosenberg 		if (inode)						\
93*508ec739SDaniel Rosenberg 			exfat_resolve_path_parent(&path_resolve_ctx,	\
94*508ec739SDaniel Rosenberg 					    (iter)->parent, inode);	\
95*508ec739SDaniel Rosenberg 		else							\
96*508ec739SDaniel Rosenberg 			exfat_resolve_path(&path_resolve_ctx,		\
97*508ec739SDaniel Rosenberg 				     (iter)->parent);			\
98*508ec739SDaniel Rosenberg 		exfat_repair_ask(&exfat_fsck, code,			\
99*508ec739SDaniel Rosenberg 				 "ERROR: %s: " fmt " at %#" PRIx64,	\
100*508ec739SDaniel Rosenberg 				 path_resolve_ctx.local_path,		\
101*508ec739SDaniel Rosenberg 				 ##__VA_ARGS__,				\
102*508ec739SDaniel Rosenberg 				 exfat_de_iter_device_offset(iter));	\
103*508ec739SDaniel Rosenberg })
104*508ec739SDaniel Rosenberg 
check_clus_chain(struct exfat_de_iter * de_iter,struct exfat_inode * node)105*508ec739SDaniel Rosenberg static int check_clus_chain(struct exfat_de_iter *de_iter,
106*508ec739SDaniel Rosenberg 				struct exfat_inode *node)
107*508ec739SDaniel Rosenberg {
108*508ec739SDaniel Rosenberg 	struct exfat *exfat = de_iter->exfat;
109*508ec739SDaniel Rosenberg 	struct exfat_dentry *stream_de;
110*508ec739SDaniel Rosenberg 	clus_t clus, prev, next, new_clus;
111*508ec739SDaniel Rosenberg 	uint64_t count, max_count;
112*508ec739SDaniel Rosenberg 	int err;
113*508ec739SDaniel Rosenberg 
114*508ec739SDaniel Rosenberg 	clus = node->first_clus;
115*508ec739SDaniel Rosenberg 	prev = EXFAT_EOF_CLUSTER;
116*508ec739SDaniel Rosenberg 	count = 0;
117*508ec739SDaniel Rosenberg 	max_count = DIV_ROUND_UP(node->size, exfat->clus_size);
118*508ec739SDaniel Rosenberg 
119*508ec739SDaniel Rosenberg 	if (node->size == 0 && node->first_clus == EXFAT_FREE_CLUSTER) {
120*508ec739SDaniel Rosenberg 		/* locate a cluster for the empty dir if the dir starts with EXFAT_FREE_CLUSTER */
121*508ec739SDaniel Rosenberg 		if (node->attr & ATTR_SUBDIR) {
122*508ec739SDaniel Rosenberg 			if (repair_file_ask(de_iter, node,
123*508ec739SDaniel Rosenberg 					ER_DE_FIRST_CLUS,
124*508ec739SDaniel Rosenberg 					"size %#" PRIx64 ", but the first cluster %#x",
125*508ec739SDaniel Rosenberg 					node->size, node->first_clus))
126*508ec739SDaniel Rosenberg 				goto allocate_cluster;
127*508ec739SDaniel Rosenberg 			return -EINVAL;
128*508ec739SDaniel Rosenberg 		}
129*508ec739SDaniel Rosenberg 		return 0;
130*508ec739SDaniel Rosenberg 	}
131*508ec739SDaniel Rosenberg 	/* the first cluster is wrong */
132*508ec739SDaniel Rosenberg 	if ((node->size == 0 && node->first_clus != EXFAT_FREE_CLUSTER) ||
133*508ec739SDaniel Rosenberg 	    (node->size > 0 && !exfat_heap_clus(exfat, node->first_clus))) {
134*508ec739SDaniel Rosenberg 		if (repair_file_ask(de_iter, node,
135*508ec739SDaniel Rosenberg 				    ER_FILE_FIRST_CLUS,
136*508ec739SDaniel Rosenberg 				    "size %#" PRIx64 ", but the first cluster %#x",
137*508ec739SDaniel Rosenberg 				    node->size, node->first_clus))
138*508ec739SDaniel Rosenberg 			goto truncate_file;
139*508ec739SDaniel Rosenberg 		else
140*508ec739SDaniel Rosenberg 			return -EINVAL;
141*508ec739SDaniel Rosenberg 	}
142*508ec739SDaniel Rosenberg 
143*508ec739SDaniel Rosenberg 	while (clus != EXFAT_EOF_CLUSTER) {
144*508ec739SDaniel Rosenberg 		if (count >= max_count) {
145*508ec739SDaniel Rosenberg 			if (node->is_contiguous)
146*508ec739SDaniel Rosenberg 				break;
147*508ec739SDaniel Rosenberg 			if (repair_file_ask(de_iter, node,
148*508ec739SDaniel Rosenberg 					    ER_FILE_SMALLER_SIZE,
149*508ec739SDaniel Rosenberg 					    "more clusters are allocated. truncate to %"
150*508ec739SDaniel Rosenberg 					    PRIu64 " bytes",
151*508ec739SDaniel Rosenberg 					    count * exfat->clus_size))
152*508ec739SDaniel Rosenberg 				goto truncate_file;
153*508ec739SDaniel Rosenberg 			else
154*508ec739SDaniel Rosenberg 				return -EINVAL;
155*508ec739SDaniel Rosenberg 		}
156*508ec739SDaniel Rosenberg 
157*508ec739SDaniel Rosenberg 		/*
158*508ec739SDaniel Rosenberg 		 * This cluster is already allocated. it may be shared with
159*508ec739SDaniel Rosenberg 		 * the other file, or there is a loop in cluster chain.
160*508ec739SDaniel Rosenberg 		 */
161*508ec739SDaniel Rosenberg 		if (exfat_bitmap_get(exfat->alloc_bitmap, clus)) {
162*508ec739SDaniel Rosenberg 			if (repair_file_ask(de_iter, node,
163*508ec739SDaniel Rosenberg 					    ER_FILE_DUPLICATED_CLUS,
164*508ec739SDaniel Rosenberg 					    "cluster is already allocated for the other file. truncated to %"
165*508ec739SDaniel Rosenberg 					    PRIu64 " bytes",
166*508ec739SDaniel Rosenberg 					    count * exfat->clus_size))
167*508ec739SDaniel Rosenberg 				goto truncate_file;
168*508ec739SDaniel Rosenberg 			else
169*508ec739SDaniel Rosenberg 				return -EINVAL;
170*508ec739SDaniel Rosenberg 		}
171*508ec739SDaniel Rosenberg 
172*508ec739SDaniel Rosenberg 		if (!exfat_bitmap_get(exfat->disk_bitmap, clus)) {
173*508ec739SDaniel Rosenberg 			if (!repair_file_ask(de_iter, node,
174*508ec739SDaniel Rosenberg 					     ER_FILE_INVALID_CLUS,
175*508ec739SDaniel Rosenberg 					     "cluster %#x is marked as free",
176*508ec739SDaniel Rosenberg 					     clus))
177*508ec739SDaniel Rosenberg 				return -EINVAL;
178*508ec739SDaniel Rosenberg 		}
179*508ec739SDaniel Rosenberg 
180*508ec739SDaniel Rosenberg 		/* This cluster is allocated or not */
181*508ec739SDaniel Rosenberg 		if (exfat_get_inode_next_clus(exfat, node, clus, &next))
182*508ec739SDaniel Rosenberg 			goto truncate_file;
183*508ec739SDaniel Rosenberg 		if (next == EXFAT_BAD_CLUSTER) {
184*508ec739SDaniel Rosenberg 			if (repair_file_ask(de_iter, node,
185*508ec739SDaniel Rosenberg 					    ER_FILE_INVALID_CLUS,
186*508ec739SDaniel Rosenberg 					    "BAD cluster. truncate to %"
187*508ec739SDaniel Rosenberg 					    PRIu64 " bytes",
188*508ec739SDaniel Rosenberg 					    count * exfat->clus_size))
189*508ec739SDaniel Rosenberg 				goto truncate_file;
190*508ec739SDaniel Rosenberg 			else
191*508ec739SDaniel Rosenberg 				return -EINVAL;
192*508ec739SDaniel Rosenberg 		} else if (!node->is_contiguous) {
193*508ec739SDaniel Rosenberg 			if (next != EXFAT_EOF_CLUSTER &&
194*508ec739SDaniel Rosenberg 			    !exfat_heap_clus(exfat, next)) {
195*508ec739SDaniel Rosenberg 				if (repair_file_ask(de_iter, node,
196*508ec739SDaniel Rosenberg 						    ER_FILE_INVALID_CLUS,
197*508ec739SDaniel Rosenberg 						    "broken cluster chain. truncate to %"
198*508ec739SDaniel Rosenberg 						    PRIu64 " bytes",
199*508ec739SDaniel Rosenberg 						    (count + 1) * exfat->clus_size)) {
200*508ec739SDaniel Rosenberg 					count++;
201*508ec739SDaniel Rosenberg 					prev = clus;
202*508ec739SDaniel Rosenberg 					exfat_bitmap_set(exfat->alloc_bitmap,
203*508ec739SDaniel Rosenberg 							 clus);
204*508ec739SDaniel Rosenberg 					goto truncate_file;
205*508ec739SDaniel Rosenberg 				} else {
206*508ec739SDaniel Rosenberg 					return -EINVAL;
207*508ec739SDaniel Rosenberg 				}
208*508ec739SDaniel Rosenberg 			}
209*508ec739SDaniel Rosenberg 		}
210*508ec739SDaniel Rosenberg 
211*508ec739SDaniel Rosenberg 		count++;
212*508ec739SDaniel Rosenberg 		exfat_bitmap_set(exfat->alloc_bitmap, clus);
213*508ec739SDaniel Rosenberg 		prev = clus;
214*508ec739SDaniel Rosenberg 		clus = next;
215*508ec739SDaniel Rosenberg 	}
216*508ec739SDaniel Rosenberg 
217*508ec739SDaniel Rosenberg 	if (count < max_count) {
218*508ec739SDaniel Rosenberg 		if (repair_file_ask(de_iter, node, ER_FILE_LARGER_SIZE,
219*508ec739SDaniel Rosenberg 				    "less clusters are allocated. truncates to %"
220*508ec739SDaniel Rosenberg 				    PRIu64 " bytes",
221*508ec739SDaniel Rosenberg 				    count * exfat->clus_size))
222*508ec739SDaniel Rosenberg 			goto truncate_file;
223*508ec739SDaniel Rosenberg 		else
224*508ec739SDaniel Rosenberg 			return -EINVAL;
225*508ec739SDaniel Rosenberg 	}
226*508ec739SDaniel Rosenberg 
227*508ec739SDaniel Rosenberg 	return 0;
228*508ec739SDaniel Rosenberg allocate_cluster:
229*508ec739SDaniel Rosenberg 	exfat_de_iter_get_dirty(de_iter, 1, &stream_de);
230*508ec739SDaniel Rosenberg 	err = exfat_find_free_cluster(exfat, exfat->start_clu, &new_clus);
231*508ec739SDaniel Rosenberg 	if (err) {
232*508ec739SDaniel Rosenberg 		exfat->start_clu = EXFAT_FIRST_CLUSTER;
233*508ec739SDaniel Rosenberg 		exfat_err("failed to find a free cluster\n");
234*508ec739SDaniel Rosenberg 		return -ENOSPC;
235*508ec739SDaniel Rosenberg 	}
236*508ec739SDaniel Rosenberg 	exfat->start_clu = new_clus;
237*508ec739SDaniel Rosenberg 
238*508ec739SDaniel Rosenberg 	if (exfat_set_fat(exfat, new_clus, EXFAT_EOF_CLUSTER))
239*508ec739SDaniel Rosenberg 		return -EIO;
240*508ec739SDaniel Rosenberg 
241*508ec739SDaniel Rosenberg 	/* zero out the new cluster */
242*508ec739SDaniel Rosenberg 	if (exfat_write(exfat->blk_dev->dev_fd, exfat->zero_cluster,
243*508ec739SDaniel Rosenberg 			exfat->clus_size, exfat_c2o(exfat, new_clus)) !=
244*508ec739SDaniel Rosenberg 			(ssize_t)exfat->clus_size) {
245*508ec739SDaniel Rosenberg 		exfat_err("failed to fill new cluster with zeroes\n");
246*508ec739SDaniel Rosenberg 		return -EIO;
247*508ec739SDaniel Rosenberg 	}
248*508ec739SDaniel Rosenberg 
249*508ec739SDaniel Rosenberg 	/* modify the number of cluster form 0 to 1 */
250*508ec739SDaniel Rosenberg 	count = 1;
251*508ec739SDaniel Rosenberg 	stream_de->stream_start_clu = cpu_to_le32(new_clus);
252*508ec739SDaniel Rosenberg 	stream_de->stream_size = cpu_to_le64(count * exfat->clus_size);
253*508ec739SDaniel Rosenberg 	stream_de->stream_valid_size = cpu_to_le64(count * exfat->clus_size);
254*508ec739SDaniel Rosenberg 	stream_de->dentry.stream.flags |= EXFAT_SF_CONTIGUOUS;
255*508ec739SDaniel Rosenberg 	node->first_clus = new_clus;
256*508ec739SDaniel Rosenberg 	node->size = count * exfat->clus_size;
257*508ec739SDaniel Rosenberg 	node->is_contiguous = true;
258*508ec739SDaniel Rosenberg 	exfat_bitmap_set(exfat->alloc_bitmap, new_clus);
259*508ec739SDaniel Rosenberg 	return 1;
260*508ec739SDaniel Rosenberg truncate_file:
261*508ec739SDaniel Rosenberg 	node->size = count * exfat->clus_size;
262*508ec739SDaniel Rosenberg 	if (!exfat_heap_clus(exfat, prev))
263*508ec739SDaniel Rosenberg 		node->first_clus = EXFAT_FREE_CLUSTER;
264*508ec739SDaniel Rosenberg 
265*508ec739SDaniel Rosenberg 	exfat_de_iter_get_dirty(de_iter, 1, &stream_de);
266*508ec739SDaniel Rosenberg 	if (count * exfat->clus_size <
267*508ec739SDaniel Rosenberg 	    le64_to_cpu(stream_de->stream_valid_size))
268*508ec739SDaniel Rosenberg 		stream_de->stream_valid_size = cpu_to_le64(
269*508ec739SDaniel Rosenberg 							   count * exfat->clus_size);
270*508ec739SDaniel Rosenberg 	if (!exfat_heap_clus(exfat, prev))
271*508ec739SDaniel Rosenberg 		stream_de->stream_start_clu = EXFAT_FREE_CLUSTER;
272*508ec739SDaniel Rosenberg 	stream_de->stream_size = cpu_to_le64(
273*508ec739SDaniel Rosenberg 					     count * exfat->clus_size);
274*508ec739SDaniel Rosenberg 
275*508ec739SDaniel Rosenberg 	/* remaining clusters will be freed while FAT is compared with
276*508ec739SDaniel Rosenberg 	 * alloc_bitmap.
277*508ec739SDaniel Rosenberg 	 */
278*508ec739SDaniel Rosenberg 	if (!node->is_contiguous && exfat_heap_clus(exfat, prev)) {
279*508ec739SDaniel Rosenberg 		if (exfat_set_fat(exfat, prev, EXFAT_EOF_CLUSTER))
280*508ec739SDaniel Rosenberg 			return -EIO;
281*508ec739SDaniel Rosenberg 	}
282*508ec739SDaniel Rosenberg 	return 1;
283*508ec739SDaniel Rosenberg }
284*508ec739SDaniel Rosenberg 
root_check_clus_chain(struct exfat * exfat,struct exfat_inode * node,clus_t * clus_count)285*508ec739SDaniel Rosenberg static int root_check_clus_chain(struct exfat *exfat,
286*508ec739SDaniel Rosenberg 				 struct exfat_inode *node,
287*508ec739SDaniel Rosenberg 				 clus_t *clus_count)
288*508ec739SDaniel Rosenberg {
289*508ec739SDaniel Rosenberg 	clus_t clus, next, prev = EXFAT_EOF_CLUSTER;
290*508ec739SDaniel Rosenberg 
291*508ec739SDaniel Rosenberg 	if (!exfat_heap_clus(exfat, node->first_clus))
292*508ec739SDaniel Rosenberg 		goto out_trunc;
293*508ec739SDaniel Rosenberg 
294*508ec739SDaniel Rosenberg 	clus = node->first_clus;
295*508ec739SDaniel Rosenberg 	*clus_count = 0;
296*508ec739SDaniel Rosenberg 
297*508ec739SDaniel Rosenberg 	do {
298*508ec739SDaniel Rosenberg 		if (exfat_bitmap_get(exfat->alloc_bitmap, clus)) {
299*508ec739SDaniel Rosenberg 			if (exfat_repair_ask(&exfat_fsck,
300*508ec739SDaniel Rosenberg 					     ER_FILE_DUPLICATED_CLUS,
301*508ec739SDaniel Rosenberg 					     "ERROR: the cluster chain of root is cyclic"))
302*508ec739SDaniel Rosenberg 				goto out_trunc;
303*508ec739SDaniel Rosenberg 			return -EINVAL;
304*508ec739SDaniel Rosenberg 		}
305*508ec739SDaniel Rosenberg 
306*508ec739SDaniel Rosenberg 		exfat_bitmap_set(exfat->alloc_bitmap, clus);
307*508ec739SDaniel Rosenberg 
308*508ec739SDaniel Rosenberg 		if (exfat_get_inode_next_clus(exfat, node, clus, &next)) {
309*508ec739SDaniel Rosenberg 			exfat_err("ERROR: failed to read the fat entry of root");
310*508ec739SDaniel Rosenberg 			goto out_trunc;
311*508ec739SDaniel Rosenberg 		}
312*508ec739SDaniel Rosenberg 
313*508ec739SDaniel Rosenberg 		if (next != EXFAT_EOF_CLUSTER && !exfat_heap_clus(exfat, next)) {
314*508ec739SDaniel Rosenberg 			if (exfat_repair_ask(&exfat_fsck,
315*508ec739SDaniel Rosenberg 					     ER_FILE_INVALID_CLUS,
316*508ec739SDaniel Rosenberg 					     "ERROR: the cluster chain of root is broken")) {
317*508ec739SDaniel Rosenberg 				if (next != EXFAT_BAD_CLUSTER) {
318*508ec739SDaniel Rosenberg 					prev = clus;
319*508ec739SDaniel Rosenberg 					(*clus_count)++;
320*508ec739SDaniel Rosenberg 				}
321*508ec739SDaniel Rosenberg 				goto out_trunc;
322*508ec739SDaniel Rosenberg 			}
323*508ec739SDaniel Rosenberg 			return -EINVAL;
324*508ec739SDaniel Rosenberg 		}
325*508ec739SDaniel Rosenberg 
326*508ec739SDaniel Rosenberg 		prev = clus;
327*508ec739SDaniel Rosenberg 		clus = next;
328*508ec739SDaniel Rosenberg 		(*clus_count)++;
329*508ec739SDaniel Rosenberg 	} while (clus != EXFAT_EOF_CLUSTER);
330*508ec739SDaniel Rosenberg 
331*508ec739SDaniel Rosenberg 	return 0;
332*508ec739SDaniel Rosenberg out_trunc:
333*508ec739SDaniel Rosenberg 	if (!exfat_heap_clus(exfat, prev)) {
334*508ec739SDaniel Rosenberg 		exfat_err("ERROR: the start cluster of root is wrong\n");
335*508ec739SDaniel Rosenberg 		return -EINVAL;
336*508ec739SDaniel Rosenberg 	}
337*508ec739SDaniel Rosenberg 	node->size = *clus_count * exfat->clus_size;
338*508ec739SDaniel Rosenberg 	return exfat_set_fat(exfat, prev, EXFAT_EOF_CLUSTER);
339*508ec739SDaniel Rosenberg }
340*508ec739SDaniel Rosenberg 
boot_region_checksum(int dev_fd,int bs_offset,unsigned int sect_size)341*508ec739SDaniel Rosenberg static int boot_region_checksum(int dev_fd,
342*508ec739SDaniel Rosenberg 				int bs_offset, unsigned int sect_size)
343*508ec739SDaniel Rosenberg {
344*508ec739SDaniel Rosenberg 	void *sect;
345*508ec739SDaniel Rosenberg 	unsigned int i;
346*508ec739SDaniel Rosenberg 	uint32_t checksum;
347*508ec739SDaniel Rosenberg 	int ret = 0;
348*508ec739SDaniel Rosenberg 
349*508ec739SDaniel Rosenberg 	sect = malloc(sect_size);
350*508ec739SDaniel Rosenberg 	if (!sect)
351*508ec739SDaniel Rosenberg 		return -ENOMEM;
352*508ec739SDaniel Rosenberg 
353*508ec739SDaniel Rosenberg 	checksum = 0;
354*508ec739SDaniel Rosenberg 	for (i = 0; i < 11; i++) {
355*508ec739SDaniel Rosenberg 		if (exfat_read(dev_fd, sect, sect_size,
356*508ec739SDaniel Rosenberg 				bs_offset * sect_size + i * sect_size) !=
357*508ec739SDaniel Rosenberg 				(ssize_t)sect_size) {
358*508ec739SDaniel Rosenberg 			exfat_err("failed to read boot region\n");
359*508ec739SDaniel Rosenberg 			ret = -EIO;
360*508ec739SDaniel Rosenberg 			goto out;
361*508ec739SDaniel Rosenberg 		}
362*508ec739SDaniel Rosenberg 		boot_calc_checksum(sect, sect_size, i == 0, &checksum);
363*508ec739SDaniel Rosenberg 	}
364*508ec739SDaniel Rosenberg 
365*508ec739SDaniel Rosenberg 	if (exfat_read(dev_fd, sect, sect_size,
366*508ec739SDaniel Rosenberg 			bs_offset * sect_size + 11 * sect_size) !=
367*508ec739SDaniel Rosenberg 			(ssize_t)sect_size) {
368*508ec739SDaniel Rosenberg 		exfat_err("failed to read a boot checksum sector\n");
369*508ec739SDaniel Rosenberg 		ret = -EIO;
370*508ec739SDaniel Rosenberg 		goto out;
371*508ec739SDaniel Rosenberg 	}
372*508ec739SDaniel Rosenberg 
373*508ec739SDaniel Rosenberg 	for (i = 0; i < sect_size/sizeof(checksum); i++) {
374*508ec739SDaniel Rosenberg 		if (le32_to_cpu(((__le32 *)sect)[i]) != checksum) {
375*508ec739SDaniel Rosenberg 			exfat_err("checksum of boot region is not correct. %#x, but expected %#x\n",
376*508ec739SDaniel Rosenberg 				le32_to_cpu(((__le32 *)sect)[i]), checksum);
377*508ec739SDaniel Rosenberg 			ret = -EINVAL;
378*508ec739SDaniel Rosenberg 			goto out;
379*508ec739SDaniel Rosenberg 		}
380*508ec739SDaniel Rosenberg 	}
381*508ec739SDaniel Rosenberg out:
382*508ec739SDaniel Rosenberg 	free(sect);
383*508ec739SDaniel Rosenberg 	return ret;
384*508ec739SDaniel Rosenberg }
385*508ec739SDaniel Rosenberg 
exfat_mark_volume_dirty(struct exfat * exfat,bool dirty)386*508ec739SDaniel Rosenberg static int exfat_mark_volume_dirty(struct exfat *exfat, bool dirty)
387*508ec739SDaniel Rosenberg {
388*508ec739SDaniel Rosenberg 	uint16_t flags;
389*508ec739SDaniel Rosenberg 
390*508ec739SDaniel Rosenberg 	flags = le16_to_cpu(exfat->bs->bsx.vol_flags);
391*508ec739SDaniel Rosenberg 	if (dirty)
392*508ec739SDaniel Rosenberg 		flags |= 0x02;
393*508ec739SDaniel Rosenberg 	else
394*508ec739SDaniel Rosenberg 		flags &= ~0x02;
395*508ec739SDaniel Rosenberg 
396*508ec739SDaniel Rosenberg 	exfat->bs->bsx.vol_flags = cpu_to_le16(flags);
397*508ec739SDaniel Rosenberg 	if (exfat_write(exfat->blk_dev->dev_fd, exfat->bs,
398*508ec739SDaniel Rosenberg 			sizeof(struct pbr), 0) != (ssize_t)sizeof(struct pbr)) {
399*508ec739SDaniel Rosenberg 		exfat_err("failed to set VolumeDirty\n");
400*508ec739SDaniel Rosenberg 		return -EIO;
401*508ec739SDaniel Rosenberg 	}
402*508ec739SDaniel Rosenberg 
403*508ec739SDaniel Rosenberg 	if (fsync(exfat->blk_dev->dev_fd) != 0) {
404*508ec739SDaniel Rosenberg 		exfat_err("failed to set VolumeDirty\n");
405*508ec739SDaniel Rosenberg 		return -EIO;
406*508ec739SDaniel Rosenberg 	}
407*508ec739SDaniel Rosenberg 	return 0;
408*508ec739SDaniel Rosenberg }
409*508ec739SDaniel Rosenberg 
read_boot_region(struct exfat_blk_dev * bd,struct pbr ** pbr,int bs_offset,unsigned int sect_size,bool verbose)410*508ec739SDaniel Rosenberg static int read_boot_region(struct exfat_blk_dev *bd, struct pbr **pbr,
411*508ec739SDaniel Rosenberg 			    int bs_offset, unsigned int sect_size,
412*508ec739SDaniel Rosenberg 			    bool verbose)
413*508ec739SDaniel Rosenberg {
414*508ec739SDaniel Rosenberg 	struct pbr *bs;
415*508ec739SDaniel Rosenberg 	int ret = -EINVAL;
416*508ec739SDaniel Rosenberg 
417*508ec739SDaniel Rosenberg 	*pbr = NULL;
418*508ec739SDaniel Rosenberg 	bs = (struct pbr *)malloc(sizeof(struct pbr));
419*508ec739SDaniel Rosenberg 	if (!bs) {
420*508ec739SDaniel Rosenberg 		exfat_err("failed to allocate memory\n");
421*508ec739SDaniel Rosenberg 		return -ENOMEM;
422*508ec739SDaniel Rosenberg 	}
423*508ec739SDaniel Rosenberg 
424*508ec739SDaniel Rosenberg 	if (exfat_read(bd->dev_fd, bs, sizeof(*bs),
425*508ec739SDaniel Rosenberg 			bs_offset * sect_size) != (ssize_t)sizeof(*bs)) {
426*508ec739SDaniel Rosenberg 		exfat_err("failed to read a boot sector\n");
427*508ec739SDaniel Rosenberg 		ret = -EIO;
428*508ec739SDaniel Rosenberg 		goto err;
429*508ec739SDaniel Rosenberg 	}
430*508ec739SDaniel Rosenberg 
431*508ec739SDaniel Rosenberg 	if (memcmp(bs->bpb.oem_name, "EXFAT   ", 8) != 0) {
432*508ec739SDaniel Rosenberg 		if (verbose)
433*508ec739SDaniel Rosenberg 			exfat_err("failed to find exfat file system\n");
434*508ec739SDaniel Rosenberg 		goto err;
435*508ec739SDaniel Rosenberg 	}
436*508ec739SDaniel Rosenberg 
437*508ec739SDaniel Rosenberg 	ret = boot_region_checksum(bd->dev_fd, bs_offset, sect_size);
438*508ec739SDaniel Rosenberg 	if (ret < 0)
439*508ec739SDaniel Rosenberg 		goto err;
440*508ec739SDaniel Rosenberg 
441*508ec739SDaniel Rosenberg 	ret = -EINVAL;
442*508ec739SDaniel Rosenberg 	if (EXFAT_SECTOR_SIZE(bs) < 512 || EXFAT_SECTOR_SIZE(bs) > 4 * KB) {
443*508ec739SDaniel Rosenberg 		if (verbose)
444*508ec739SDaniel Rosenberg 			exfat_err("too small or big sector size: %d\n",
445*508ec739SDaniel Rosenberg 				  EXFAT_SECTOR_SIZE(bs));
446*508ec739SDaniel Rosenberg 		goto err;
447*508ec739SDaniel Rosenberg 	}
448*508ec739SDaniel Rosenberg 
449*508ec739SDaniel Rosenberg 	if (EXFAT_CLUSTER_SIZE(bs) > 32 * MB) {
450*508ec739SDaniel Rosenberg 		if (verbose)
451*508ec739SDaniel Rosenberg 			exfat_err("too big cluster size: %d\n",
452*508ec739SDaniel Rosenberg 				  EXFAT_CLUSTER_SIZE(bs));
453*508ec739SDaniel Rosenberg 		goto err;
454*508ec739SDaniel Rosenberg 	}
455*508ec739SDaniel Rosenberg 
456*508ec739SDaniel Rosenberg 	if (bs->bsx.fs_version[1] != 1 || bs->bsx.fs_version[0] != 0) {
457*508ec739SDaniel Rosenberg 		if (verbose)
458*508ec739SDaniel Rosenberg 			exfat_err("unsupported exfat version: %d.%d\n",
459*508ec739SDaniel Rosenberg 				  bs->bsx.fs_version[1], bs->bsx.fs_version[0]);
460*508ec739SDaniel Rosenberg 		goto err;
461*508ec739SDaniel Rosenberg 	}
462*508ec739SDaniel Rosenberg 
463*508ec739SDaniel Rosenberg 	if (bs->bsx.num_fats != 1) {
464*508ec739SDaniel Rosenberg 		if (verbose)
465*508ec739SDaniel Rosenberg 			exfat_err("unsupported FAT count: %d\n",
466*508ec739SDaniel Rosenberg 				  bs->bsx.num_fats);
467*508ec739SDaniel Rosenberg 		goto err;
468*508ec739SDaniel Rosenberg 	}
469*508ec739SDaniel Rosenberg 
470*508ec739SDaniel Rosenberg 	if (le64_to_cpu(bs->bsx.vol_length) * EXFAT_SECTOR_SIZE(bs) >
471*508ec739SDaniel Rosenberg 			bd->size) {
472*508ec739SDaniel Rosenberg 		if (verbose)
473*508ec739SDaniel Rosenberg 			exfat_err("too large sector count: %" PRIu64 ", expected: %llu\n",
474*508ec739SDaniel Rosenberg 				  le64_to_cpu(bs->bsx.vol_length),
475*508ec739SDaniel Rosenberg 				  bd->num_sectors);
476*508ec739SDaniel Rosenberg 		goto err;
477*508ec739SDaniel Rosenberg 	}
478*508ec739SDaniel Rosenberg 
479*508ec739SDaniel Rosenberg 	if (le32_to_cpu(bs->bsx.clu_count) * EXFAT_CLUSTER_SIZE(bs) >
480*508ec739SDaniel Rosenberg 			bd->size) {
481*508ec739SDaniel Rosenberg 		if (verbose)
482*508ec739SDaniel Rosenberg 			exfat_err("too large cluster count: %u, expected: %u\n",
483*508ec739SDaniel Rosenberg 				  le32_to_cpu(bs->bsx.clu_count),
484*508ec739SDaniel Rosenberg 				  bd->num_clusters);
485*508ec739SDaniel Rosenberg 		goto err;
486*508ec739SDaniel Rosenberg 	}
487*508ec739SDaniel Rosenberg 
488*508ec739SDaniel Rosenberg 	*pbr = bs;
489*508ec739SDaniel Rosenberg 	return 0;
490*508ec739SDaniel Rosenberg err:
491*508ec739SDaniel Rosenberg 	free(bs);
492*508ec739SDaniel Rosenberg 	return ret;
493*508ec739SDaniel Rosenberg }
494*508ec739SDaniel Rosenberg 
restore_boot_region(struct exfat_blk_dev * bd,unsigned int sect_size)495*508ec739SDaniel Rosenberg static int restore_boot_region(struct exfat_blk_dev *bd, unsigned int sect_size)
496*508ec739SDaniel Rosenberg {
497*508ec739SDaniel Rosenberg 	int i;
498*508ec739SDaniel Rosenberg 	char *sector;
499*508ec739SDaniel Rosenberg 	int ret;
500*508ec739SDaniel Rosenberg 
501*508ec739SDaniel Rosenberg 	sector = malloc(sect_size);
502*508ec739SDaniel Rosenberg 	if (!sector)
503*508ec739SDaniel Rosenberg 		return -ENOMEM;
504*508ec739SDaniel Rosenberg 
505*508ec739SDaniel Rosenberg 	for (i = 0; i < 12; i++) {
506*508ec739SDaniel Rosenberg 		if (exfat_read(bd->dev_fd, sector, sect_size,
507*508ec739SDaniel Rosenberg 				BACKUP_BOOT_SEC_IDX * sect_size +
508*508ec739SDaniel Rosenberg 				i * sect_size) !=
509*508ec739SDaniel Rosenberg 				(ssize_t)sect_size) {
510*508ec739SDaniel Rosenberg 			ret = -EIO;
511*508ec739SDaniel Rosenberg 			goto free_sector;
512*508ec739SDaniel Rosenberg 		}
513*508ec739SDaniel Rosenberg 		if (i == 0)
514*508ec739SDaniel Rosenberg 			((struct pbr *)sector)->bsx.perc_in_use = 0xff;
515*508ec739SDaniel Rosenberg 
516*508ec739SDaniel Rosenberg 		if (exfat_write(bd->dev_fd, sector, sect_size,
517*508ec739SDaniel Rosenberg 				BOOT_SEC_IDX * sect_size +
518*508ec739SDaniel Rosenberg 				i * sect_size) !=
519*508ec739SDaniel Rosenberg 				(ssize_t)sect_size) {
520*508ec739SDaniel Rosenberg 			ret = -EIO;
521*508ec739SDaniel Rosenberg 			goto free_sector;
522*508ec739SDaniel Rosenberg 		}
523*508ec739SDaniel Rosenberg 	}
524*508ec739SDaniel Rosenberg 
525*508ec739SDaniel Rosenberg 	if (fsync(bd->dev_fd)) {
526*508ec739SDaniel Rosenberg 		ret = -EIO;
527*508ec739SDaniel Rosenberg 		goto free_sector;
528*508ec739SDaniel Rosenberg 	}
529*508ec739SDaniel Rosenberg 	ret = 0;
530*508ec739SDaniel Rosenberg 
531*508ec739SDaniel Rosenberg free_sector:
532*508ec739SDaniel Rosenberg 	free(sector);
533*508ec739SDaniel Rosenberg 	return ret;
534*508ec739SDaniel Rosenberg }
535*508ec739SDaniel Rosenberg 
exfat_boot_region_check(struct exfat_blk_dev * blkdev,struct pbr ** bs,bool ignore_bad_fs_name)536*508ec739SDaniel Rosenberg static int exfat_boot_region_check(struct exfat_blk_dev *blkdev,
537*508ec739SDaniel Rosenberg 				   struct pbr **bs,
538*508ec739SDaniel Rosenberg 				   bool ignore_bad_fs_name)
539*508ec739SDaniel Rosenberg {
540*508ec739SDaniel Rosenberg 	struct pbr *boot_sect;
541*508ec739SDaniel Rosenberg 	unsigned int sect_size;
542*508ec739SDaniel Rosenberg 	int ret;
543*508ec739SDaniel Rosenberg 
544*508ec739SDaniel Rosenberg 	/* First, find out the exfat sector size */
545*508ec739SDaniel Rosenberg 	boot_sect = malloc(sizeof(*boot_sect));
546*508ec739SDaniel Rosenberg 	if (boot_sect == NULL)
547*508ec739SDaniel Rosenberg 		return -ENOMEM;
548*508ec739SDaniel Rosenberg 
549*508ec739SDaniel Rosenberg 	if (exfat_read(blkdev->dev_fd, boot_sect,
550*508ec739SDaniel Rosenberg 		       sizeof(*boot_sect), 0) != (ssize_t)sizeof(*boot_sect)) {
551*508ec739SDaniel Rosenberg 		exfat_err("failed to read Main boot sector\n");
552*508ec739SDaniel Rosenberg 		free(boot_sect);
553*508ec739SDaniel Rosenberg 		return -EIO;
554*508ec739SDaniel Rosenberg 	}
555*508ec739SDaniel Rosenberg 
556*508ec739SDaniel Rosenberg 	if (memcmp(boot_sect->bpb.oem_name, "EXFAT   ", 8) != 0 &&
557*508ec739SDaniel Rosenberg 	    !ignore_bad_fs_name) {
558*508ec739SDaniel Rosenberg 		exfat_err("Bad fs_name in boot sector, which does not describe a valid exfat filesystem\n");
559*508ec739SDaniel Rosenberg 		free(boot_sect);
560*508ec739SDaniel Rosenberg 		return -ENOTSUP;
561*508ec739SDaniel Rosenberg 	}
562*508ec739SDaniel Rosenberg 
563*508ec739SDaniel Rosenberg 	sect_size = 1 << boot_sect->bsx.sect_size_bits;
564*508ec739SDaniel Rosenberg 	free(boot_sect);
565*508ec739SDaniel Rosenberg 
566*508ec739SDaniel Rosenberg 	/* check boot regions */
567*508ec739SDaniel Rosenberg 	ret = read_boot_region(blkdev, bs,
568*508ec739SDaniel Rosenberg 			       BOOT_SEC_IDX, sect_size, true);
569*508ec739SDaniel Rosenberg 	if (ret == -EINVAL &&
570*508ec739SDaniel Rosenberg 	    exfat_repair_ask(&exfat_fsck, ER_BS_BOOT_REGION,
571*508ec739SDaniel Rosenberg 			     "boot region is corrupted. try to restore the region from backup"
572*508ec739SDaniel Rosenberg 				)) {
573*508ec739SDaniel Rosenberg 		const unsigned int sector_sizes[] = {512, 4096, 1024, 2048};
574*508ec739SDaniel Rosenberg 		unsigned int i;
575*508ec739SDaniel Rosenberg 
576*508ec739SDaniel Rosenberg 		if (sect_size >= 512 && sect_size <= EXFAT_MAX_SECTOR_SIZE) {
577*508ec739SDaniel Rosenberg 			ret = read_boot_region(blkdev, bs,
578*508ec739SDaniel Rosenberg 					       BACKUP_BOOT_SEC_IDX, sect_size,
579*508ec739SDaniel Rosenberg 					       false);
580*508ec739SDaniel Rosenberg 			if (!ret)
581*508ec739SDaniel Rosenberg 				goto restore;
582*508ec739SDaniel Rosenberg 		}
583*508ec739SDaniel Rosenberg 
584*508ec739SDaniel Rosenberg 		for (i = 0; i < sizeof(sector_sizes)/sizeof(sector_sizes[0]); i++) {
585*508ec739SDaniel Rosenberg 			if (sector_sizes[i] == sect_size)
586*508ec739SDaniel Rosenberg 				continue;
587*508ec739SDaniel Rosenberg 
588*508ec739SDaniel Rosenberg 			ret = read_boot_region(blkdev, bs,
589*508ec739SDaniel Rosenberg 					       BACKUP_BOOT_SEC_IDX,
590*508ec739SDaniel Rosenberg 					       sector_sizes[i], false);
591*508ec739SDaniel Rosenberg 			if (!ret) {
592*508ec739SDaniel Rosenberg 				sect_size = sector_sizes[i];
593*508ec739SDaniel Rosenberg 				goto restore;
594*508ec739SDaniel Rosenberg 			}
595*508ec739SDaniel Rosenberg 		}
596*508ec739SDaniel Rosenberg 		exfat_err("backup boot region is also corrupted\n");
597*508ec739SDaniel Rosenberg 	}
598*508ec739SDaniel Rosenberg 
599*508ec739SDaniel Rosenberg 	return ret;
600*508ec739SDaniel Rosenberg restore:
601*508ec739SDaniel Rosenberg 	ret = restore_boot_region(blkdev, sect_size);
602*508ec739SDaniel Rosenberg 	if (ret) {
603*508ec739SDaniel Rosenberg 		exfat_err("failed to restore boot region from backup\n");
604*508ec739SDaniel Rosenberg 		free(*bs);
605*508ec739SDaniel Rosenberg 		*bs = NULL;
606*508ec739SDaniel Rosenberg 	}
607*508ec739SDaniel Rosenberg 	return ret;
608*508ec739SDaniel Rosenberg }
609*508ec739SDaniel Rosenberg 
file_calc_checksum(struct exfat_de_iter * iter)610*508ec739SDaniel Rosenberg static uint16_t file_calc_checksum(struct exfat_de_iter *iter)
611*508ec739SDaniel Rosenberg {
612*508ec739SDaniel Rosenberg 	uint16_t checksum;
613*508ec739SDaniel Rosenberg 	struct exfat_dentry *file_de, *de;
614*508ec739SDaniel Rosenberg 	int i;
615*508ec739SDaniel Rosenberg 
616*508ec739SDaniel Rosenberg 	checksum = 0;
617*508ec739SDaniel Rosenberg 	exfat_de_iter_get(iter, 0, &file_de);
618*508ec739SDaniel Rosenberg 
619*508ec739SDaniel Rosenberg 	exfat_calc_dentry_checksum(file_de, &checksum, true);
620*508ec739SDaniel Rosenberg 	for (i = 1; i <= file_de->file_num_ext; i++) {
621*508ec739SDaniel Rosenberg 		exfat_de_iter_get(iter, i, &de);
622*508ec739SDaniel Rosenberg 		exfat_calc_dentry_checksum(de, &checksum, false);
623*508ec739SDaniel Rosenberg 	}
624*508ec739SDaniel Rosenberg 	return checksum;
625*508ec739SDaniel Rosenberg }
626*508ec739SDaniel Rosenberg 
627*508ec739SDaniel Rosenberg /*
628*508ec739SDaniel Rosenberg  * return 0 if there are no errors, or 1 if errors are fixed, or
629*508ec739SDaniel Rosenberg  * an error code
630*508ec739SDaniel Rosenberg  */
check_inode(struct exfat_de_iter * iter,struct exfat_inode * node)631*508ec739SDaniel Rosenberg static int check_inode(struct exfat_de_iter *iter, struct exfat_inode *node)
632*508ec739SDaniel Rosenberg {
633*508ec739SDaniel Rosenberg 	struct exfat *exfat = iter->exfat;
634*508ec739SDaniel Rosenberg 	struct exfat_dentry *dentry;
635*508ec739SDaniel Rosenberg 	int ret = 0;
636*508ec739SDaniel Rosenberg 	uint16_t checksum;
637*508ec739SDaniel Rosenberg 	bool valid = true;
638*508ec739SDaniel Rosenberg 
639*508ec739SDaniel Rosenberg 	ret = check_clus_chain(iter, node);
640*508ec739SDaniel Rosenberg 	if (ret < 0)
641*508ec739SDaniel Rosenberg 		return ret;
642*508ec739SDaniel Rosenberg 
643*508ec739SDaniel Rosenberg 	if (node->size > le32_to_cpu(exfat->bs->bsx.clu_count) *
644*508ec739SDaniel Rosenberg 				(uint64_t)exfat->clus_size) {
645*508ec739SDaniel Rosenberg 		fsck_err(iter->parent, node,
646*508ec739SDaniel Rosenberg 			"size %" PRIu64 " is greater than cluster heap\n",
647*508ec739SDaniel Rosenberg 			node->size);
648*508ec739SDaniel Rosenberg 		valid = false;
649*508ec739SDaniel Rosenberg 	}
650*508ec739SDaniel Rosenberg 
651*508ec739SDaniel Rosenberg 	if (node->size == 0 && node->is_contiguous) {
652*508ec739SDaniel Rosenberg 		if (repair_file_ask(iter, node, ER_FILE_ZERO_NOFAT,
653*508ec739SDaniel Rosenberg 				"empty, but has no Fat chain")) {
654*508ec739SDaniel Rosenberg 			exfat_de_iter_get_dirty(iter, 1, &dentry);
655*508ec739SDaniel Rosenberg 			dentry->stream_flags &= ~EXFAT_SF_CONTIGUOUS;
656*508ec739SDaniel Rosenberg 			ret = 1;
657*508ec739SDaniel Rosenberg 		} else
658*508ec739SDaniel Rosenberg 			valid = false;
659*508ec739SDaniel Rosenberg 	}
660*508ec739SDaniel Rosenberg 
661*508ec739SDaniel Rosenberg 	if ((node->attr & ATTR_SUBDIR) &&
662*508ec739SDaniel Rosenberg 			node->size % exfat->clus_size != 0) {
663*508ec739SDaniel Rosenberg 		fsck_err(iter->parent, node,
664*508ec739SDaniel Rosenberg 			"directory size %" PRIu64 " is not divisible by %d\n",
665*508ec739SDaniel Rosenberg 			node->size, exfat->clus_size);
666*508ec739SDaniel Rosenberg 		valid = false;
667*508ec739SDaniel Rosenberg 	}
668*508ec739SDaniel Rosenberg 
669*508ec739SDaniel Rosenberg 	checksum = file_calc_checksum(iter);
670*508ec739SDaniel Rosenberg 	exfat_de_iter_get(iter, 0, &dentry);
671*508ec739SDaniel Rosenberg 	if (checksum != le16_to_cpu(dentry->file_checksum)) {
672*508ec739SDaniel Rosenberg 		exfat_de_iter_get_dirty(iter, 0, &dentry);
673*508ec739SDaniel Rosenberg 		dentry->file_checksum = cpu_to_le16(checksum);
674*508ec739SDaniel Rosenberg 		ret = 1;
675*508ec739SDaniel Rosenberg 	}
676*508ec739SDaniel Rosenberg 
677*508ec739SDaniel Rosenberg 	return valid ? ret : -EINVAL;
678*508ec739SDaniel Rosenberg }
679*508ec739SDaniel Rosenberg 
check_name_dentry_set(struct exfat_de_iter * iter,struct exfat_inode * inode)680*508ec739SDaniel Rosenberg static int check_name_dentry_set(struct exfat_de_iter *iter,
681*508ec739SDaniel Rosenberg 				 struct exfat_inode *inode)
682*508ec739SDaniel Rosenberg {
683*508ec739SDaniel Rosenberg 	struct exfat_dentry *stream_de;
684*508ec739SDaniel Rosenberg 	size_t name_len;
685*508ec739SDaniel Rosenberg 	__u16 hash;
686*508ec739SDaniel Rosenberg 
687*508ec739SDaniel Rosenberg 	exfat_de_iter_get(iter, 1, &stream_de);
688*508ec739SDaniel Rosenberg 
689*508ec739SDaniel Rosenberg 	name_len = exfat_utf16_len(inode->name, NAME_BUFFER_SIZE);
690*508ec739SDaniel Rosenberg 	if (stream_de->stream_name_len != name_len) {
691*508ec739SDaniel Rosenberg 		if (repair_file_ask(iter, NULL, ER_DE_NAME_LEN,
692*508ec739SDaniel Rosenberg 				    "the name length of a file is wrong")) {
693*508ec739SDaniel Rosenberg 			exfat_de_iter_get_dirty(iter, 1, &stream_de);
694*508ec739SDaniel Rosenberg 			stream_de->stream_name_len = (__u8)name_len;
695*508ec739SDaniel Rosenberg 		} else {
696*508ec739SDaniel Rosenberg 			return -EINVAL;
697*508ec739SDaniel Rosenberg 		}
698*508ec739SDaniel Rosenberg 	}
699*508ec739SDaniel Rosenberg 
700*508ec739SDaniel Rosenberg 	hash = exfat_calc_name_hash(iter->exfat, inode->name, (int)name_len);
701*508ec739SDaniel Rosenberg 	if (cpu_to_le16(hash) != stream_de->stream_name_hash) {
702*508ec739SDaniel Rosenberg 		if (repair_file_ask(iter, NULL, ER_DE_NAME_HASH,
703*508ec739SDaniel Rosenberg 				    "the name hash of a file is wrong")) {
704*508ec739SDaniel Rosenberg 			exfat_de_iter_get_dirty(iter, 1, &stream_de);
705*508ec739SDaniel Rosenberg 			stream_de->stream_name_hash = cpu_to_le16(hash);
706*508ec739SDaniel Rosenberg 		} else {
707*508ec739SDaniel Rosenberg 			return -EINVAL;
708*508ec739SDaniel Rosenberg 		}
709*508ec739SDaniel Rosenberg 	}
710*508ec739SDaniel Rosenberg 	return 0;
711*508ec739SDaniel Rosenberg }
712*508ec739SDaniel Rosenberg 
check_bad_char(char w)713*508ec739SDaniel Rosenberg static int check_bad_char(char w)
714*508ec739SDaniel Rosenberg {
715*508ec739SDaniel Rosenberg 	return (w < 0x0020) || (w == '*') || (w == '?') || (w == '<') ||
716*508ec739SDaniel Rosenberg 		(w == '>') || (w == '|') || (w == '"') || (w == ':') ||
717*508ec739SDaniel Rosenberg 		(w == '/') || (w == '\\');
718*508ec739SDaniel Rosenberg }
719*508ec739SDaniel Rosenberg 
get_rename_from_user(struct exfat_de_iter * iter)720*508ec739SDaniel Rosenberg static char *get_rename_from_user(struct exfat_de_iter *iter)
721*508ec739SDaniel Rosenberg {
722*508ec739SDaniel Rosenberg 	char *rename = malloc(ENTRY_NAME_MAX + 2);
723*508ec739SDaniel Rosenberg 
724*508ec739SDaniel Rosenberg 	if (!rename)
725*508ec739SDaniel Rosenberg 		return NULL;
726*508ec739SDaniel Rosenberg 
727*508ec739SDaniel Rosenberg retry:
728*508ec739SDaniel Rosenberg 	/* +2 means LF(Line Feed) and NULL terminator */
729*508ec739SDaniel Rosenberg 	memset(rename, 0x1, ENTRY_NAME_MAX + 2);
730*508ec739SDaniel Rosenberg 	printf("New name: ");
731*508ec739SDaniel Rosenberg 	if (fgets(rename, ENTRY_NAME_MAX + 2, stdin)) {
732*508ec739SDaniel Rosenberg 		int i, len, err;
733*508ec739SDaniel Rosenberg 		struct exfat_lookup_filter filter;
734*508ec739SDaniel Rosenberg 
735*508ec739SDaniel Rosenberg 		len = strlen(rename);
736*508ec739SDaniel Rosenberg 		/* Remove LF in filename */
737*508ec739SDaniel Rosenberg 		rename[len - 1] = '\0';
738*508ec739SDaniel Rosenberg 		for (i = 0; i < len - 1; i++) {
739*508ec739SDaniel Rosenberg 			if (check_bad_char(rename[i])) {
740*508ec739SDaniel Rosenberg 				printf("filename contain invalid character(%c)\n", rename[i]);
741*508ec739SDaniel Rosenberg 				goto retry;
742*508ec739SDaniel Rosenberg 			}
743*508ec739SDaniel Rosenberg 		}
744*508ec739SDaniel Rosenberg 
745*508ec739SDaniel Rosenberg 		exfat_de_iter_flush(iter);
746*508ec739SDaniel Rosenberg 		err = exfat_lookup_file(iter->exfat, iter->parent, rename, &filter);
747*508ec739SDaniel Rosenberg 		if (!err) {
748*508ec739SDaniel Rosenberg 			printf("file(%s) already exists, retry to insert name\n", rename);
749*508ec739SDaniel Rosenberg 			goto retry;
750*508ec739SDaniel Rosenberg 		}
751*508ec739SDaniel Rosenberg 	}
752*508ec739SDaniel Rosenberg 
753*508ec739SDaniel Rosenberg 	return rename;
754*508ec739SDaniel Rosenberg }
755*508ec739SDaniel Rosenberg 
generate_rename(struct exfat_de_iter * iter)756*508ec739SDaniel Rosenberg static char *generate_rename(struct exfat_de_iter *iter)
757*508ec739SDaniel Rosenberg {
758*508ec739SDaniel Rosenberg 	char *rename;
759*508ec739SDaniel Rosenberg 
760*508ec739SDaniel Rosenberg 	if (iter->dot_name_num > DOT_NAME_NUM_MAX)
761*508ec739SDaniel Rosenberg 		return NULL;
762*508ec739SDaniel Rosenberg 
763*508ec739SDaniel Rosenberg 	rename = malloc(ENTRY_NAME_MAX + 1);
764*508ec739SDaniel Rosenberg 	if (!rename)
765*508ec739SDaniel Rosenberg 		return NULL;
766*508ec739SDaniel Rosenberg 
767*508ec739SDaniel Rosenberg 	while (1) {
768*508ec739SDaniel Rosenberg 		struct exfat_lookup_filter filter;
769*508ec739SDaniel Rosenberg 		int err;
770*508ec739SDaniel Rosenberg 
771*508ec739SDaniel Rosenberg 		snprintf(rename, ENTRY_NAME_MAX + 1, "FILE%07d.CHK",
772*508ec739SDaniel Rosenberg 			 iter->dot_name_num++);
773*508ec739SDaniel Rosenberg 		err = exfat_lookup_file(iter->exfat, iter->parent, rename,
774*508ec739SDaniel Rosenberg 					&filter);
775*508ec739SDaniel Rosenberg 		if (!err)
776*508ec739SDaniel Rosenberg 			continue;
777*508ec739SDaniel Rosenberg 		break;
778*508ec739SDaniel Rosenberg 	}
779*508ec739SDaniel Rosenberg 
780*508ec739SDaniel Rosenberg 	return rename;
781*508ec739SDaniel Rosenberg }
782*508ec739SDaniel Rosenberg 
783*508ec739SDaniel Rosenberg const __le16 MSDOS_DOT[ENTRY_NAME_MAX] = {cpu_to_le16(46), 0, };
784*508ec739SDaniel Rosenberg const __le16 MSDOS_DOTDOT[ENTRY_NAME_MAX] = {cpu_to_le16(46), cpu_to_le16(46), 0, };
785*508ec739SDaniel Rosenberg 
handle_dot_dotdot_filename(struct exfat_de_iter * iter,struct exfat_dentry * dentry,int strm_name_len)786*508ec739SDaniel Rosenberg static int handle_dot_dotdot_filename(struct exfat_de_iter *iter,
787*508ec739SDaniel Rosenberg 				      struct exfat_dentry *dentry,
788*508ec739SDaniel Rosenberg 				      int strm_name_len)
789*508ec739SDaniel Rosenberg {
790*508ec739SDaniel Rosenberg 	char *filename;
791*508ec739SDaniel Rosenberg 	char error_msg[150];
792*508ec739SDaniel Rosenberg 	int num;
793*508ec739SDaniel Rosenberg 
794*508ec739SDaniel Rosenberg 	if (!memcmp(dentry->name_unicode, MSDOS_DOT, strm_name_len * 2))
795*508ec739SDaniel Rosenberg 		filename = ".";
796*508ec739SDaniel Rosenberg 	else if (!memcmp(dentry->name_unicode, MSDOS_DOTDOT,
797*508ec739SDaniel Rosenberg 			 strm_name_len * 2))
798*508ec739SDaniel Rosenberg 		filename = "..";
799*508ec739SDaniel Rosenberg 	else
800*508ec739SDaniel Rosenberg 		return 0;
801*508ec739SDaniel Rosenberg 
802*508ec739SDaniel Rosenberg 	sprintf(error_msg, "ERROR: '%s' filename is not allowed.\n"
803*508ec739SDaniel Rosenberg 			" [1] Insert the name you want to rename.\n"
804*508ec739SDaniel Rosenberg 			" [2] Automatically renames filename.\n"
805*508ec739SDaniel Rosenberg 			" [3] Bypass this check(No repair)\n", filename);
806*508ec739SDaniel Rosenberg ask_again:
807*508ec739SDaniel Rosenberg 	num = exfat_repair_ask(&exfat_fsck, ER_DE_DOT_NAME,
808*508ec739SDaniel Rosenberg 			       error_msg);
809*508ec739SDaniel Rosenberg 	if (num) {
810*508ec739SDaniel Rosenberg 		__le16 utf16_name[ENTRY_NAME_MAX];
811*508ec739SDaniel Rosenberg 		char *rename = NULL;
812*508ec739SDaniel Rosenberg 		__u16 hash;
813*508ec739SDaniel Rosenberg 		struct exfat_dentry *stream_de;
814*508ec739SDaniel Rosenberg 		int name_len, ret;
815*508ec739SDaniel Rosenberg 
816*508ec739SDaniel Rosenberg 		switch (num) {
817*508ec739SDaniel Rosenberg 		case 1:
818*508ec739SDaniel Rosenberg 			rename = get_rename_from_user(iter);
819*508ec739SDaniel Rosenberg 			break;
820*508ec739SDaniel Rosenberg 		case 2:
821*508ec739SDaniel Rosenberg 			rename = generate_rename(iter);
822*508ec739SDaniel Rosenberg 			break;
823*508ec739SDaniel Rosenberg 		case 3:
824*508ec739SDaniel Rosenberg 			break;
825*508ec739SDaniel Rosenberg 		default:
826*508ec739SDaniel Rosenberg 			exfat_info("select 1 or 2 number instead of %d\n", num);
827*508ec739SDaniel Rosenberg 			goto ask_again;
828*508ec739SDaniel Rosenberg 		}
829*508ec739SDaniel Rosenberg 
830*508ec739SDaniel Rosenberg 		if (!rename)
831*508ec739SDaniel Rosenberg 			return -EINVAL;
832*508ec739SDaniel Rosenberg 
833*508ec739SDaniel Rosenberg 		exfat_info("%s filename is renamed to %s\n", filename, rename);
834*508ec739SDaniel Rosenberg 
835*508ec739SDaniel Rosenberg 		exfat_de_iter_get_dirty(iter, 2, &dentry);
836*508ec739SDaniel Rosenberg 
837*508ec739SDaniel Rosenberg 		memset(utf16_name, 0, sizeof(utf16_name));
838*508ec739SDaniel Rosenberg 		ret = exfat_utf16_enc(rename, utf16_name, sizeof(utf16_name));
839*508ec739SDaniel Rosenberg 		free(rename);
840*508ec739SDaniel Rosenberg 		if (ret < 0)
841*508ec739SDaniel Rosenberg 			return ret;
842*508ec739SDaniel Rosenberg 
843*508ec739SDaniel Rosenberg 		memcpy(dentry->name_unicode, utf16_name, ENTRY_NAME_MAX * 2);
844*508ec739SDaniel Rosenberg 		name_len = exfat_utf16_len(utf16_name, ENTRY_NAME_MAX * 2);
845*508ec739SDaniel Rosenberg 		hash = exfat_calc_name_hash(iter->exfat, utf16_name, (int)name_len);
846*508ec739SDaniel Rosenberg 		exfat_de_iter_get_dirty(iter, 1, &stream_de);
847*508ec739SDaniel Rosenberg 		stream_de->stream_name_len = (__u8)name_len;
848*508ec739SDaniel Rosenberg 		stream_de->stream_name_hash = cpu_to_le16(hash);
849*508ec739SDaniel Rosenberg 	}
850*508ec739SDaniel Rosenberg 
851*508ec739SDaniel Rosenberg 	return 0;
852*508ec739SDaniel Rosenberg }
853*508ec739SDaniel Rosenberg 
read_file_dentry_set(struct exfat_de_iter * iter,struct exfat_inode ** new_node,int * skip_dentries)854*508ec739SDaniel Rosenberg static int read_file_dentry_set(struct exfat_de_iter *iter,
855*508ec739SDaniel Rosenberg 				struct exfat_inode **new_node, int *skip_dentries)
856*508ec739SDaniel Rosenberg {
857*508ec739SDaniel Rosenberg 	struct exfat_dentry *file_de, *stream_de, *dentry;
858*508ec739SDaniel Rosenberg 	struct exfat_inode *node = NULL;
859*508ec739SDaniel Rosenberg 	int i, ret;
860*508ec739SDaniel Rosenberg 	bool need_delete = false;
861*508ec739SDaniel Rosenberg 	uint16_t checksum;
862*508ec739SDaniel Rosenberg 
863*508ec739SDaniel Rosenberg 	ret = exfat_de_iter_get(iter, 0, &file_de);
864*508ec739SDaniel Rosenberg 	if (ret || file_de->type != EXFAT_FILE) {
865*508ec739SDaniel Rosenberg 		exfat_err("failed to get file dentry\n");
866*508ec739SDaniel Rosenberg 		return -EINVAL;
867*508ec739SDaniel Rosenberg 	}
868*508ec739SDaniel Rosenberg 
869*508ec739SDaniel Rosenberg 	checksum = file_calc_checksum(iter);
870*508ec739SDaniel Rosenberg 	if (checksum != le16_to_cpu(file_de->file_checksum)) {
871*508ec739SDaniel Rosenberg 		if (repair_file_ask(iter, NULL, ER_DE_CHECKSUM,
872*508ec739SDaniel Rosenberg 				    "the checksum of a file is wrong"))
873*508ec739SDaniel Rosenberg 			need_delete = true;
874*508ec739SDaniel Rosenberg 		*skip_dentries = 1;
875*508ec739SDaniel Rosenberg 		goto skip_dset;
876*508ec739SDaniel Rosenberg 	}
877*508ec739SDaniel Rosenberg 
878*508ec739SDaniel Rosenberg 	if (file_de->file_num_ext < 2) {
879*508ec739SDaniel Rosenberg 		if (repair_file_ask(iter, NULL, ER_DE_SECONDARY_COUNT,
880*508ec739SDaniel Rosenberg 				    "a file has too few secondary count. %d",
881*508ec739SDaniel Rosenberg 				    file_de->file_num_ext))
882*508ec739SDaniel Rosenberg 			need_delete = true;
883*508ec739SDaniel Rosenberg 		*skip_dentries = 1;
884*508ec739SDaniel Rosenberg 		goto skip_dset;
885*508ec739SDaniel Rosenberg 	}
886*508ec739SDaniel Rosenberg 
887*508ec739SDaniel Rosenberg 	ret = exfat_de_iter_get(iter, 1, &stream_de);
888*508ec739SDaniel Rosenberg 	if (ret || stream_de->type != EXFAT_STREAM) {
889*508ec739SDaniel Rosenberg 		if (repair_file_ask(iter, NULL, ER_DE_STREAM,
890*508ec739SDaniel Rosenberg 				    "failed to get stream dentry"))
891*508ec739SDaniel Rosenberg 			need_delete = true;
892*508ec739SDaniel Rosenberg 		*skip_dentries = 2;
893*508ec739SDaniel Rosenberg 		goto skip_dset;
894*508ec739SDaniel Rosenberg 	}
895*508ec739SDaniel Rosenberg 
896*508ec739SDaniel Rosenberg 	*new_node = NULL;
897*508ec739SDaniel Rosenberg 	node = exfat_alloc_inode(le16_to_cpu(file_de->file_attr));
898*508ec739SDaniel Rosenberg 	if (!node)
899*508ec739SDaniel Rosenberg 		return -ENOMEM;
900*508ec739SDaniel Rosenberg 
901*508ec739SDaniel Rosenberg 	for (i = 2; i <= file_de->file_num_ext; i++) {
902*508ec739SDaniel Rosenberg 		ret = exfat_de_iter_get(iter, i, &dentry);
903*508ec739SDaniel Rosenberg 		if (ret || dentry->type != EXFAT_NAME) {
904*508ec739SDaniel Rosenberg 			if (i > 2 && repair_file_ask(iter, NULL, ER_DE_NAME,
905*508ec739SDaniel Rosenberg 						     "failed to get name dentry")) {
906*508ec739SDaniel Rosenberg 				exfat_de_iter_get_dirty(iter, 0, &file_de);
907*508ec739SDaniel Rosenberg 				file_de->file_num_ext = i - 1;
908*508ec739SDaniel Rosenberg 				break;
909*508ec739SDaniel Rosenberg 			}
910*508ec739SDaniel Rosenberg 			*skip_dentries = i + 1;
911*508ec739SDaniel Rosenberg 			goto skip_dset;
912*508ec739SDaniel Rosenberg 		}
913*508ec739SDaniel Rosenberg 
914*508ec739SDaniel Rosenberg 		memcpy(node->name +
915*508ec739SDaniel Rosenberg 		       (i - 2) * ENTRY_NAME_MAX, dentry->name_unicode,
916*508ec739SDaniel Rosenberg 		       sizeof(dentry->name_unicode));
917*508ec739SDaniel Rosenberg 	}
918*508ec739SDaniel Rosenberg 
919*508ec739SDaniel Rosenberg 	ret = check_name_dentry_set(iter, node);
920*508ec739SDaniel Rosenberg 	if (ret) {
921*508ec739SDaniel Rosenberg 		*skip_dentries = file_de->file_num_ext + 1;
922*508ec739SDaniel Rosenberg 		goto skip_dset;
923*508ec739SDaniel Rosenberg 	}
924*508ec739SDaniel Rosenberg 
925*508ec739SDaniel Rosenberg 	if (file_de->file_num_ext == 2 && stream_de->stream_name_len <= 2) {
926*508ec739SDaniel Rosenberg 		ret = handle_dot_dotdot_filename(iter, dentry,
927*508ec739SDaniel Rosenberg 				stream_de->stream_name_len);
928*508ec739SDaniel Rosenberg 		if (ret < 0) {
929*508ec739SDaniel Rosenberg 			*skip_dentries = file_de->file_num_ext + 1;
930*508ec739SDaniel Rosenberg 			goto skip_dset;
931*508ec739SDaniel Rosenberg 		}
932*508ec739SDaniel Rosenberg 	}
933*508ec739SDaniel Rosenberg 
934*508ec739SDaniel Rosenberg 	node->first_clus = le32_to_cpu(stream_de->stream_start_clu);
935*508ec739SDaniel Rosenberg 	node->is_contiguous =
936*508ec739SDaniel Rosenberg 		((stream_de->stream_flags & EXFAT_SF_CONTIGUOUS) != 0);
937*508ec739SDaniel Rosenberg 	node->size = le64_to_cpu(stream_de->stream_size);
938*508ec739SDaniel Rosenberg 
939*508ec739SDaniel Rosenberg 	if (node->size < le64_to_cpu(stream_de->stream_valid_size)) {
940*508ec739SDaniel Rosenberg 		*skip_dentries = file_de->file_num_ext + 1;
941*508ec739SDaniel Rosenberg 		if (repair_file_ask(iter, node, ER_FILE_VALID_SIZE,
942*508ec739SDaniel Rosenberg 				    "valid size %" PRIu64 " greater than size %" PRIu64,
943*508ec739SDaniel Rosenberg 				    le64_to_cpu(stream_de->stream_valid_size),
944*508ec739SDaniel Rosenberg 				    node->size)) {
945*508ec739SDaniel Rosenberg 			exfat_de_iter_get_dirty(iter, 1, &stream_de);
946*508ec739SDaniel Rosenberg 			stream_de->stream_valid_size =
947*508ec739SDaniel Rosenberg 					stream_de->stream_size;
948*508ec739SDaniel Rosenberg 		} else {
949*508ec739SDaniel Rosenberg 			*skip_dentries = file_de->file_num_ext + 1;
950*508ec739SDaniel Rosenberg 			goto skip_dset;
951*508ec739SDaniel Rosenberg 		}
952*508ec739SDaniel Rosenberg 	}
953*508ec739SDaniel Rosenberg 
954*508ec739SDaniel Rosenberg 	*skip_dentries = (file_de->file_num_ext + 1);
955*508ec739SDaniel Rosenberg 	*new_node = node;
956*508ec739SDaniel Rosenberg 	return 0;
957*508ec739SDaniel Rosenberg skip_dset:
958*508ec739SDaniel Rosenberg 	if (need_delete) {
959*508ec739SDaniel Rosenberg 		exfat_de_iter_get_dirty(iter, 0, &dentry);
960*508ec739SDaniel Rosenberg 		dentry->type &= EXFAT_DELETE;
961*508ec739SDaniel Rosenberg 	}
962*508ec739SDaniel Rosenberg 	for (i = 1; i < *skip_dentries; i++) {
963*508ec739SDaniel Rosenberg 		exfat_de_iter_get(iter, i, &dentry);
964*508ec739SDaniel Rosenberg 		if (dentry->type == EXFAT_FILE)
965*508ec739SDaniel Rosenberg 			break;
966*508ec739SDaniel Rosenberg 		if (need_delete) {
967*508ec739SDaniel Rosenberg 			exfat_de_iter_get_dirty(iter, i, &dentry);
968*508ec739SDaniel Rosenberg 			dentry->type &= EXFAT_DELETE;
969*508ec739SDaniel Rosenberg 		}
970*508ec739SDaniel Rosenberg 	}
971*508ec739SDaniel Rosenberg 	*skip_dentries = i;
972*508ec739SDaniel Rosenberg 	*new_node = NULL;
973*508ec739SDaniel Rosenberg 	exfat_free_inode(node);
974*508ec739SDaniel Rosenberg 	return need_delete ? 1 : -EINVAL;
975*508ec739SDaniel Rosenberg }
976*508ec739SDaniel Rosenberg 
read_file(struct exfat_de_iter * de_iter,struct exfat_inode ** new_node,int * dentry_count)977*508ec739SDaniel Rosenberg static int read_file(struct exfat_de_iter *de_iter,
978*508ec739SDaniel Rosenberg 		struct exfat_inode **new_node, int *dentry_count)
979*508ec739SDaniel Rosenberg {
980*508ec739SDaniel Rosenberg 	struct exfat_inode *node;
981*508ec739SDaniel Rosenberg 	int ret;
982*508ec739SDaniel Rosenberg 
983*508ec739SDaniel Rosenberg 	*new_node = NULL;
984*508ec739SDaniel Rosenberg 
985*508ec739SDaniel Rosenberg 	ret = read_file_dentry_set(de_iter, &node, dentry_count);
986*508ec739SDaniel Rosenberg 	if (ret)
987*508ec739SDaniel Rosenberg 		return ret;
988*508ec739SDaniel Rosenberg 
989*508ec739SDaniel Rosenberg 	ret = check_inode(de_iter, node);
990*508ec739SDaniel Rosenberg 	if (ret < 0) {
991*508ec739SDaniel Rosenberg 		exfat_free_inode(node);
992*508ec739SDaniel Rosenberg 		return -EINVAL;
993*508ec739SDaniel Rosenberg 	}
994*508ec739SDaniel Rosenberg 
995*508ec739SDaniel Rosenberg 	if (node->attr & ATTR_SUBDIR)
996*508ec739SDaniel Rosenberg 		exfat_stat.dir_count++;
997*508ec739SDaniel Rosenberg 	else
998*508ec739SDaniel Rosenberg 		exfat_stat.file_count++;
999*508ec739SDaniel Rosenberg 	*new_node = node;
1000*508ec739SDaniel Rosenberg 	return ret;
1001*508ec739SDaniel Rosenberg }
1002*508ec739SDaniel Rosenberg 
read_bitmap(struct exfat * exfat)1003*508ec739SDaniel Rosenberg static int read_bitmap(struct exfat *exfat)
1004*508ec739SDaniel Rosenberg {
1005*508ec739SDaniel Rosenberg 	struct exfat_lookup_filter filter = {
1006*508ec739SDaniel Rosenberg 		.in.type	= EXFAT_BITMAP,
1007*508ec739SDaniel Rosenberg 		.in.filter	= NULL,
1008*508ec739SDaniel Rosenberg 		.in.param	= NULL,
1009*508ec739SDaniel Rosenberg 	};
1010*508ec739SDaniel Rosenberg 	struct exfat_dentry *dentry;
1011*508ec739SDaniel Rosenberg 	int retval;
1012*508ec739SDaniel Rosenberg 
1013*508ec739SDaniel Rosenberg 	retval = exfat_lookup_dentry_set(exfat, exfat->root, &filter);
1014*508ec739SDaniel Rosenberg 	if (retval)
1015*508ec739SDaniel Rosenberg 		return retval;
1016*508ec739SDaniel Rosenberg 
1017*508ec739SDaniel Rosenberg 	dentry = filter.out.dentry_set;
1018*508ec739SDaniel Rosenberg 	exfat_debug("start cluster %#x, size %#" PRIx64 "\n",
1019*508ec739SDaniel Rosenberg 			le32_to_cpu(dentry->bitmap_start_clu),
1020*508ec739SDaniel Rosenberg 			le64_to_cpu(dentry->bitmap_size));
1021*508ec739SDaniel Rosenberg 
1022*508ec739SDaniel Rosenberg 	if (le64_to_cpu(dentry->bitmap_size) <
1023*508ec739SDaniel Rosenberg 			DIV_ROUND_UP(exfat->clus_count, 8)) {
1024*508ec739SDaniel Rosenberg 		exfat_err("invalid size of allocation bitmap. 0x%" PRIx64 "\n",
1025*508ec739SDaniel Rosenberg 				le64_to_cpu(dentry->bitmap_size));
1026*508ec739SDaniel Rosenberg 		return -EINVAL;
1027*508ec739SDaniel Rosenberg 	}
1028*508ec739SDaniel Rosenberg 	if (!exfat_heap_clus(exfat, le32_to_cpu(dentry->bitmap_start_clu))) {
1029*508ec739SDaniel Rosenberg 		exfat_err("invalid start cluster of allocate bitmap. 0x%x\n",
1030*508ec739SDaniel Rosenberg 				le32_to_cpu(dentry->bitmap_start_clu));
1031*508ec739SDaniel Rosenberg 		return -EINVAL;
1032*508ec739SDaniel Rosenberg 	}
1033*508ec739SDaniel Rosenberg 
1034*508ec739SDaniel Rosenberg 	exfat->disk_bitmap_clus = le32_to_cpu(dentry->bitmap_start_clu);
1035*508ec739SDaniel Rosenberg 	exfat->disk_bitmap_size = DIV_ROUND_UP(exfat->clus_count, 8);
1036*508ec739SDaniel Rosenberg 
1037*508ec739SDaniel Rosenberg 	exfat_bitmap_set_range(exfat, exfat->alloc_bitmap,
1038*508ec739SDaniel Rosenberg 			       le64_to_cpu(dentry->bitmap_start_clu),
1039*508ec739SDaniel Rosenberg 			       DIV_ROUND_UP(exfat->disk_bitmap_size,
1040*508ec739SDaniel Rosenberg 					    exfat->clus_size));
1041*508ec739SDaniel Rosenberg 	free(filter.out.dentry_set);
1042*508ec739SDaniel Rosenberg 
1043*508ec739SDaniel Rosenberg 	if (exfat_read(exfat->blk_dev->dev_fd, exfat->disk_bitmap,
1044*508ec739SDaniel Rosenberg 			exfat->disk_bitmap_size,
1045*508ec739SDaniel Rosenberg 			exfat_c2o(exfat, exfat->disk_bitmap_clus)) !=
1046*508ec739SDaniel Rosenberg 			(ssize_t)exfat->disk_bitmap_size)
1047*508ec739SDaniel Rosenberg 		return -EIO;
1048*508ec739SDaniel Rosenberg 	return 0;
1049*508ec739SDaniel Rosenberg }
1050*508ec739SDaniel Rosenberg 
decompress_upcase_table(const __le16 * in_table,size_t in_len,__u16 * out_table,size_t out_len)1051*508ec739SDaniel Rosenberg static int decompress_upcase_table(const __le16 *in_table, size_t in_len,
1052*508ec739SDaniel Rosenberg 				   __u16 *out_table, size_t out_len)
1053*508ec739SDaniel Rosenberg {
1054*508ec739SDaniel Rosenberg 	size_t i, k;
1055*508ec739SDaniel Rosenberg 	uint16_t ch;
1056*508ec739SDaniel Rosenberg 
1057*508ec739SDaniel Rosenberg 	if (in_len > out_len)
1058*508ec739SDaniel Rosenberg 		return -E2BIG;
1059*508ec739SDaniel Rosenberg 
1060*508ec739SDaniel Rosenberg 	for (k = 0; k < out_len; k++)
1061*508ec739SDaniel Rosenberg 		out_table[k] = k;
1062*508ec739SDaniel Rosenberg 
1063*508ec739SDaniel Rosenberg 	for (i = 0, k = 0; i < in_len && k < out_len; i++) {
1064*508ec739SDaniel Rosenberg 		ch = le16_to_cpu(in_table[i]);
1065*508ec739SDaniel Rosenberg 
1066*508ec739SDaniel Rosenberg 		if (ch == 0xFFFF && i + 1 < in_len) {
1067*508ec739SDaniel Rosenberg 			uint16_t len = le16_to_cpu(in_table[++i]);
1068*508ec739SDaniel Rosenberg 
1069*508ec739SDaniel Rosenberg 			k += len;
1070*508ec739SDaniel Rosenberg 		} else {
1071*508ec739SDaniel Rosenberg 			out_table[k++] = ch;
1072*508ec739SDaniel Rosenberg 		}
1073*508ec739SDaniel Rosenberg 	}
1074*508ec739SDaniel Rosenberg 	return 0;
1075*508ec739SDaniel Rosenberg }
1076*508ec739SDaniel Rosenberg 
read_upcase_table(struct exfat * exfat)1077*508ec739SDaniel Rosenberg static int read_upcase_table(struct exfat *exfat)
1078*508ec739SDaniel Rosenberg {
1079*508ec739SDaniel Rosenberg 	struct exfat_lookup_filter filter = {
1080*508ec739SDaniel Rosenberg 		.in.type	= EXFAT_UPCASE,
1081*508ec739SDaniel Rosenberg 		.in.filter	= NULL,
1082*508ec739SDaniel Rosenberg 		.in.param	= NULL,
1083*508ec739SDaniel Rosenberg 	};
1084*508ec739SDaniel Rosenberg 	struct exfat_dentry *dentry = NULL;
1085*508ec739SDaniel Rosenberg 	__le16 *upcase = NULL;
1086*508ec739SDaniel Rosenberg 	int retval;
1087*508ec739SDaniel Rosenberg 	ssize_t size;
1088*508ec739SDaniel Rosenberg 	__le32 checksum;
1089*508ec739SDaniel Rosenberg 
1090*508ec739SDaniel Rosenberg 	retval = exfat_lookup_dentry_set(exfat, exfat->root, &filter);
1091*508ec739SDaniel Rosenberg 	if (retval)
1092*508ec739SDaniel Rosenberg 		return retval;
1093*508ec739SDaniel Rosenberg 
1094*508ec739SDaniel Rosenberg 	dentry = filter.out.dentry_set;
1095*508ec739SDaniel Rosenberg 
1096*508ec739SDaniel Rosenberg 	if (!exfat_heap_clus(exfat, le32_to_cpu(dentry->upcase_start_clu))) {
1097*508ec739SDaniel Rosenberg 		exfat_err("invalid start cluster of upcase table. 0x%x\n",
1098*508ec739SDaniel Rosenberg 			le32_to_cpu(dentry->upcase_start_clu));
1099*508ec739SDaniel Rosenberg 		retval = -EINVAL;
1100*508ec739SDaniel Rosenberg 		goto out;
1101*508ec739SDaniel Rosenberg 	}
1102*508ec739SDaniel Rosenberg 
1103*508ec739SDaniel Rosenberg 	size = (ssize_t)le64_to_cpu(dentry->upcase_size);
1104*508ec739SDaniel Rosenberg 	if (size > (ssize_t)(EXFAT_MAX_UPCASE_CHARS * sizeof(__le16)) ||
1105*508ec739SDaniel Rosenberg 			size == 0 || size % sizeof(__le16)) {
1106*508ec739SDaniel Rosenberg 		exfat_err("invalid size of upcase table. 0x%" PRIx64 "\n",
1107*508ec739SDaniel Rosenberg 			le64_to_cpu(dentry->upcase_size));
1108*508ec739SDaniel Rosenberg 		retval = -EINVAL;
1109*508ec739SDaniel Rosenberg 		goto out;
1110*508ec739SDaniel Rosenberg 	}
1111*508ec739SDaniel Rosenberg 
1112*508ec739SDaniel Rosenberg 	upcase = (__le16 *)malloc(size);
1113*508ec739SDaniel Rosenberg 	if (!upcase) {
1114*508ec739SDaniel Rosenberg 		exfat_err("failed to allocate upcase table\n");
1115*508ec739SDaniel Rosenberg 		retval = -ENOMEM;
1116*508ec739SDaniel Rosenberg 		goto out;
1117*508ec739SDaniel Rosenberg 	}
1118*508ec739SDaniel Rosenberg 
1119*508ec739SDaniel Rosenberg 	if (exfat_read(exfat->blk_dev->dev_fd, upcase, size,
1120*508ec739SDaniel Rosenberg 			exfat_c2o(exfat,
1121*508ec739SDaniel Rosenberg 			le32_to_cpu(dentry->upcase_start_clu))) != size) {
1122*508ec739SDaniel Rosenberg 		exfat_err("failed to read upcase table\n");
1123*508ec739SDaniel Rosenberg 		retval = -EIO;
1124*508ec739SDaniel Rosenberg 		goto out;
1125*508ec739SDaniel Rosenberg 	}
1126*508ec739SDaniel Rosenberg 
1127*508ec739SDaniel Rosenberg 	checksum = 0;
1128*508ec739SDaniel Rosenberg 	boot_calc_checksum((unsigned char *)upcase, size, false, &checksum);
1129*508ec739SDaniel Rosenberg 	if (le32_to_cpu(dentry->upcase_checksum) != checksum) {
1130*508ec739SDaniel Rosenberg 		exfat_err("corrupted upcase table %#x (expected: %#x)\n",
1131*508ec739SDaniel Rosenberg 			checksum, le32_to_cpu(dentry->upcase_checksum));
1132*508ec739SDaniel Rosenberg 		retval = -EINVAL;
1133*508ec739SDaniel Rosenberg 		goto out;
1134*508ec739SDaniel Rosenberg 	}
1135*508ec739SDaniel Rosenberg 
1136*508ec739SDaniel Rosenberg 	exfat_bitmap_set_range(exfat, exfat->alloc_bitmap,
1137*508ec739SDaniel Rosenberg 			       le32_to_cpu(dentry->upcase_start_clu),
1138*508ec739SDaniel Rosenberg 			       DIV_ROUND_UP(le64_to_cpu(dentry->upcase_size),
1139*508ec739SDaniel Rosenberg 					    exfat->clus_size));
1140*508ec739SDaniel Rosenberg 
1141*508ec739SDaniel Rosenberg 	exfat->upcase_table = calloc(1,
1142*508ec739SDaniel Rosenberg 				     sizeof(uint16_t) * EXFAT_UPCASE_TABLE_CHARS);
1143*508ec739SDaniel Rosenberg 	if (!exfat->upcase_table) {
1144*508ec739SDaniel Rosenberg 		retval = -EIO;
1145*508ec739SDaniel Rosenberg 		goto out;
1146*508ec739SDaniel Rosenberg 	}
1147*508ec739SDaniel Rosenberg 
1148*508ec739SDaniel Rosenberg 	decompress_upcase_table(upcase, size / 2,
1149*508ec739SDaniel Rosenberg 				exfat->upcase_table, EXFAT_UPCASE_TABLE_CHARS);
1150*508ec739SDaniel Rosenberg out:
1151*508ec739SDaniel Rosenberg 	if (dentry)
1152*508ec739SDaniel Rosenberg 		free(dentry);
1153*508ec739SDaniel Rosenberg 	if (upcase)
1154*508ec739SDaniel Rosenberg 		free(upcase);
1155*508ec739SDaniel Rosenberg 	return retval;
1156*508ec739SDaniel Rosenberg }
1157*508ec739SDaniel Rosenberg 
read_children(struct exfat_fsck * fsck,struct exfat_inode * dir)1158*508ec739SDaniel Rosenberg static int read_children(struct exfat_fsck *fsck, struct exfat_inode *dir)
1159*508ec739SDaniel Rosenberg {
1160*508ec739SDaniel Rosenberg 	struct exfat *exfat = fsck->exfat;
1161*508ec739SDaniel Rosenberg 	struct exfat_inode *node = NULL;
1162*508ec739SDaniel Rosenberg 	struct exfat_dentry *dentry;
1163*508ec739SDaniel Rosenberg 	struct exfat_de_iter *de_iter;
1164*508ec739SDaniel Rosenberg 	int dentry_count;
1165*508ec739SDaniel Rosenberg 	int ret;
1166*508ec739SDaniel Rosenberg 
1167*508ec739SDaniel Rosenberg 	de_iter = &fsck->de_iter;
1168*508ec739SDaniel Rosenberg 	ret = exfat_de_iter_init(de_iter, exfat, dir, fsck->buffer_desc);
1169*508ec739SDaniel Rosenberg 	if (ret == EOF)
1170*508ec739SDaniel Rosenberg 		return 0;
1171*508ec739SDaniel Rosenberg 	else if (ret)
1172*508ec739SDaniel Rosenberg 		return ret;
1173*508ec739SDaniel Rosenberg 
1174*508ec739SDaniel Rosenberg 	while (1) {
1175*508ec739SDaniel Rosenberg 		ret = exfat_de_iter_get(de_iter, 0, &dentry);
1176*508ec739SDaniel Rosenberg 		if (ret == EOF) {
1177*508ec739SDaniel Rosenberg 			break;
1178*508ec739SDaniel Rosenberg 		} else if (ret) {
1179*508ec739SDaniel Rosenberg 			fsck_err(dir->parent, dir,
1180*508ec739SDaniel Rosenberg 				"failed to get a dentry. %d\n", ret);
1181*508ec739SDaniel Rosenberg 			goto err;
1182*508ec739SDaniel Rosenberg 		}
1183*508ec739SDaniel Rosenberg 
1184*508ec739SDaniel Rosenberg 		dentry_count = 1;
1185*508ec739SDaniel Rosenberg 
1186*508ec739SDaniel Rosenberg 		switch (dentry->type) {
1187*508ec739SDaniel Rosenberg 		case EXFAT_FILE:
1188*508ec739SDaniel Rosenberg 			ret = read_file(de_iter, &node, &dentry_count);
1189*508ec739SDaniel Rosenberg 			if (ret < 0) {
1190*508ec739SDaniel Rosenberg 				exfat_stat.error_count++;
1191*508ec739SDaniel Rosenberg 				break;
1192*508ec739SDaniel Rosenberg 			} else if (ret) {
1193*508ec739SDaniel Rosenberg 				exfat_stat.error_count++;
1194*508ec739SDaniel Rosenberg 				exfat_stat.fixed_count++;
1195*508ec739SDaniel Rosenberg 			}
1196*508ec739SDaniel Rosenberg 
1197*508ec739SDaniel Rosenberg 			if (node) {
1198*508ec739SDaniel Rosenberg 				if ((node->attr & ATTR_SUBDIR) && node->size) {
1199*508ec739SDaniel Rosenberg 					node->parent = dir;
1200*508ec739SDaniel Rosenberg 					list_add_tail(&node->sibling,
1201*508ec739SDaniel Rosenberg 						      &dir->children);
1202*508ec739SDaniel Rosenberg 					list_add_tail(&node->list,
1203*508ec739SDaniel Rosenberg 						      &exfat->dir_list);
1204*508ec739SDaniel Rosenberg 				} else {
1205*508ec739SDaniel Rosenberg 					exfat_free_inode(node);
1206*508ec739SDaniel Rosenberg 				}
1207*508ec739SDaniel Rosenberg 			}
1208*508ec739SDaniel Rosenberg 			break;
1209*508ec739SDaniel Rosenberg 		case EXFAT_LAST:
1210*508ec739SDaniel Rosenberg 			goto out;
1211*508ec739SDaniel Rosenberg 		case EXFAT_VOLUME:
1212*508ec739SDaniel Rosenberg 		case EXFAT_BITMAP:
1213*508ec739SDaniel Rosenberg 		case EXFAT_UPCASE:
1214*508ec739SDaniel Rosenberg 			if (dir == exfat->root)
1215*508ec739SDaniel Rosenberg 				break;
1216*508ec739SDaniel Rosenberg 			/* fallthrough */
1217*508ec739SDaniel Rosenberg 		default:
1218*508ec739SDaniel Rosenberg 			if (IS_EXFAT_DELETED(dentry->type))
1219*508ec739SDaniel Rosenberg 				break;
1220*508ec739SDaniel Rosenberg 			if (repair_file_ask(de_iter, NULL, ER_DE_UNKNOWN,
1221*508ec739SDaniel Rosenberg 					    "unknown entry type %#x at %07" PRIx64,
1222*508ec739SDaniel Rosenberg 					    dentry->type,
1223*508ec739SDaniel Rosenberg 					    exfat_de_iter_file_offset(de_iter))) {
1224*508ec739SDaniel Rosenberg 				struct exfat_dentry *dentry;
1225*508ec739SDaniel Rosenberg 
1226*508ec739SDaniel Rosenberg 				exfat_de_iter_get_dirty(de_iter, 0, &dentry);
1227*508ec739SDaniel Rosenberg 				dentry->type &= EXFAT_DELETE;
1228*508ec739SDaniel Rosenberg 			}
1229*508ec739SDaniel Rosenberg 			break;
1230*508ec739SDaniel Rosenberg 		}
1231*508ec739SDaniel Rosenberg 
1232*508ec739SDaniel Rosenberg 		exfat_de_iter_advance(de_iter, dentry_count);
1233*508ec739SDaniel Rosenberg 	}
1234*508ec739SDaniel Rosenberg out:
1235*508ec739SDaniel Rosenberg 	exfat_de_iter_flush(de_iter);
1236*508ec739SDaniel Rosenberg 	return 0;
1237*508ec739SDaniel Rosenberg err:
1238*508ec739SDaniel Rosenberg 	exfat_free_children(dir, false);
1239*508ec739SDaniel Rosenberg 	INIT_LIST_HEAD(&dir->children);
1240*508ec739SDaniel Rosenberg 	exfat_de_iter_flush(de_iter);
1241*508ec739SDaniel Rosenberg 	return ret;
1242*508ec739SDaniel Rosenberg }
1243*508ec739SDaniel Rosenberg 
1244*508ec739SDaniel Rosenberg /* write bitmap segments for clusters which are marked
1245*508ec739SDaniel Rosenberg  * as free, but allocated to files.
1246*508ec739SDaniel Rosenberg  */
write_bitmap(struct exfat_fsck * fsck)1247*508ec739SDaniel Rosenberg static int write_bitmap(struct exfat_fsck *fsck)
1248*508ec739SDaniel Rosenberg {
1249*508ec739SDaniel Rosenberg 	struct exfat *exfat = fsck->exfat;
1250*508ec739SDaniel Rosenberg 	bitmap_t *disk_b, *alloc_b, *ohead_b;
1251*508ec739SDaniel Rosenberg 	off_t dev_offset;
1252*508ec739SDaniel Rosenberg 	unsigned int i, bitmap_bytes, byte_offset, write_bytes;
1253*508ec739SDaniel Rosenberg 
1254*508ec739SDaniel Rosenberg 	dev_offset = exfat_c2o(exfat, exfat->disk_bitmap_clus);
1255*508ec739SDaniel Rosenberg 	bitmap_bytes = EXFAT_BITMAP_SIZE(le32_to_cpu(exfat->bs->bsx.clu_count));
1256*508ec739SDaniel Rosenberg 
1257*508ec739SDaniel Rosenberg 	disk_b = (bitmap_t *)exfat->disk_bitmap;
1258*508ec739SDaniel Rosenberg 	alloc_b = (bitmap_t *)exfat->alloc_bitmap;
1259*508ec739SDaniel Rosenberg 	ohead_b = (bitmap_t *)exfat->ohead_bitmap;
1260*508ec739SDaniel Rosenberg 
1261*508ec739SDaniel Rosenberg 	for (i = 0; i < bitmap_bytes / sizeof(bitmap_t); i++)
1262*508ec739SDaniel Rosenberg 		ohead_b[i] = alloc_b[i] | disk_b[i];
1263*508ec739SDaniel Rosenberg 
1264*508ec739SDaniel Rosenberg 	i = 0;
1265*508ec739SDaniel Rosenberg 	while (i < bitmap_bytes / sizeof(bitmap_t)) {
1266*508ec739SDaniel Rosenberg 		if (ohead_b[i] == disk_b[i]) {
1267*508ec739SDaniel Rosenberg 			i++;
1268*508ec739SDaniel Rosenberg 			continue;
1269*508ec739SDaniel Rosenberg 		}
1270*508ec739SDaniel Rosenberg 
1271*508ec739SDaniel Rosenberg 		byte_offset = ((i * sizeof(bitmap_t)) / 512) * 512;
1272*508ec739SDaniel Rosenberg 		write_bytes = MIN(512, bitmap_bytes - byte_offset);
1273*508ec739SDaniel Rosenberg 
1274*508ec739SDaniel Rosenberg 		if (exfat_write(exfat->blk_dev->dev_fd,
1275*508ec739SDaniel Rosenberg 				(char *)ohead_b + byte_offset, write_bytes,
1276*508ec739SDaniel Rosenberg 				dev_offset + byte_offset) != (ssize_t)write_bytes)
1277*508ec739SDaniel Rosenberg 			return -EIO;
1278*508ec739SDaniel Rosenberg 
1279*508ec739SDaniel Rosenberg 		i = (byte_offset + write_bytes) / sizeof(bitmap_t);
1280*508ec739SDaniel Rosenberg 	}
1281*508ec739SDaniel Rosenberg 	return 0;
1282*508ec739SDaniel Rosenberg 
1283*508ec739SDaniel Rosenberg }
1284*508ec739SDaniel Rosenberg 
1285*508ec739SDaniel Rosenberg /*
1286*508ec739SDaniel Rosenberg  * for each directory in @dir_list.
1287*508ec739SDaniel Rosenberg  * 1. read all dentries and allocate exfat_nodes for files and directories.
1288*508ec739SDaniel Rosenberg  *    and append directory exfat_nodes to the head of @dir_list
1289*508ec739SDaniel Rosenberg  * 2. free all of file exfat_nodes.
1290*508ec739SDaniel Rosenberg  * 3. if the directory does not have children, free its exfat_node.
1291*508ec739SDaniel Rosenberg  */
exfat_filesystem_check(struct exfat_fsck * fsck)1292*508ec739SDaniel Rosenberg static int exfat_filesystem_check(struct exfat_fsck *fsck)
1293*508ec739SDaniel Rosenberg {
1294*508ec739SDaniel Rosenberg 	struct exfat *exfat = fsck->exfat;
1295*508ec739SDaniel Rosenberg 	struct exfat_inode *dir;
1296*508ec739SDaniel Rosenberg 	int ret = 0, dir_errors;
1297*508ec739SDaniel Rosenberg 
1298*508ec739SDaniel Rosenberg 	if (!exfat->root) {
1299*508ec739SDaniel Rosenberg 		exfat_err("root is NULL\n");
1300*508ec739SDaniel Rosenberg 		return -ENOENT;
1301*508ec739SDaniel Rosenberg 	}
1302*508ec739SDaniel Rosenberg 
1303*508ec739SDaniel Rosenberg 	list_add(&exfat->root->list, &exfat->dir_list);
1304*508ec739SDaniel Rosenberg 
1305*508ec739SDaniel Rosenberg 	while (!list_empty(&exfat->dir_list)) {
1306*508ec739SDaniel Rosenberg 		dir = list_entry(exfat->dir_list.next,
1307*508ec739SDaniel Rosenberg 				 struct exfat_inode, list);
1308*508ec739SDaniel Rosenberg 
1309*508ec739SDaniel Rosenberg 		if (!(dir->attr & ATTR_SUBDIR)) {
1310*508ec739SDaniel Rosenberg 			fsck_err(dir->parent, dir,
1311*508ec739SDaniel Rosenberg 				"failed to travel directories. "
1312*508ec739SDaniel Rosenberg 				"the node is not directory\n");
1313*508ec739SDaniel Rosenberg 			ret = -EINVAL;
1314*508ec739SDaniel Rosenberg 			goto out;
1315*508ec739SDaniel Rosenberg 		}
1316*508ec739SDaniel Rosenberg 
1317*508ec739SDaniel Rosenberg 		dir_errors = read_children(fsck, dir);
1318*508ec739SDaniel Rosenberg 		if (dir_errors) {
1319*508ec739SDaniel Rosenberg 			exfat_resolve_path(&path_resolve_ctx, dir);
1320*508ec739SDaniel Rosenberg 			exfat_debug("failed to check dentries: %s\n",
1321*508ec739SDaniel Rosenberg 					path_resolve_ctx.local_path);
1322*508ec739SDaniel Rosenberg 			ret = dir_errors;
1323*508ec739SDaniel Rosenberg 		}
1324*508ec739SDaniel Rosenberg 
1325*508ec739SDaniel Rosenberg 		list_del(&dir->list);
1326*508ec739SDaniel Rosenberg 		exfat_free_file_children(dir);
1327*508ec739SDaniel Rosenberg 		exfat_free_ancestors(dir);
1328*508ec739SDaniel Rosenberg 	}
1329*508ec739SDaniel Rosenberg out:
1330*508ec739SDaniel Rosenberg 	exfat_free_dir_list(exfat);
1331*508ec739SDaniel Rosenberg 	return ret;
1332*508ec739SDaniel Rosenberg }
1333*508ec739SDaniel Rosenberg 
exfat_root_dir_check(struct exfat * exfat)1334*508ec739SDaniel Rosenberg static int exfat_root_dir_check(struct exfat *exfat)
1335*508ec739SDaniel Rosenberg {
1336*508ec739SDaniel Rosenberg 	struct exfat_inode *root;
1337*508ec739SDaniel Rosenberg 	clus_t clus_count = 0;
1338*508ec739SDaniel Rosenberg 	int err;
1339*508ec739SDaniel Rosenberg 
1340*508ec739SDaniel Rosenberg 	root = exfat_alloc_inode(ATTR_SUBDIR);
1341*508ec739SDaniel Rosenberg 	if (!root)
1342*508ec739SDaniel Rosenberg 		return -ENOMEM;
1343*508ec739SDaniel Rosenberg 
1344*508ec739SDaniel Rosenberg 	exfat->root = root;
1345*508ec739SDaniel Rosenberg 	root->first_clus = le32_to_cpu(exfat->bs->bsx.root_cluster);
1346*508ec739SDaniel Rosenberg 	if (root_check_clus_chain(exfat, root, &clus_count)) {
1347*508ec739SDaniel Rosenberg 		exfat_err("failed to follow the cluster chain of root\n");
1348*508ec739SDaniel Rosenberg 		exfat_free_inode(root);
1349*508ec739SDaniel Rosenberg 		exfat->root = NULL;
1350*508ec739SDaniel Rosenberg 		return -EINVAL;
1351*508ec739SDaniel Rosenberg 	}
1352*508ec739SDaniel Rosenberg 	root->size = clus_count * exfat->clus_size;
1353*508ec739SDaniel Rosenberg 
1354*508ec739SDaniel Rosenberg 	exfat_stat.dir_count++;
1355*508ec739SDaniel Rosenberg 	exfat_debug("root directory: start cluster[0x%x] size[0x%" PRIx64 "]\n",
1356*508ec739SDaniel Rosenberg 		root->first_clus, root->size);
1357*508ec739SDaniel Rosenberg 
1358*508ec739SDaniel Rosenberg 	err = exfat_read_volume_label(exfat);
1359*508ec739SDaniel Rosenberg 	if (err && err != EOF)
1360*508ec739SDaniel Rosenberg 		exfat_err("failed to read volume label\n");
1361*508ec739SDaniel Rosenberg 	err = 0;
1362*508ec739SDaniel Rosenberg 
1363*508ec739SDaniel Rosenberg 	err = read_bitmap(exfat);
1364*508ec739SDaniel Rosenberg 	if (err) {
1365*508ec739SDaniel Rosenberg 		exfat_err("failed to read bitmap\n");
1366*508ec739SDaniel Rosenberg 		return -EINVAL;
1367*508ec739SDaniel Rosenberg 	}
1368*508ec739SDaniel Rosenberg 
1369*508ec739SDaniel Rosenberg 	err = read_upcase_table(exfat);
1370*508ec739SDaniel Rosenberg 	if (err) {
1371*508ec739SDaniel Rosenberg 		exfat_err("failed to read upcase table\n");
1372*508ec739SDaniel Rosenberg 		return -EINVAL;
1373*508ec739SDaniel Rosenberg 	}
1374*508ec739SDaniel Rosenberg 
1375*508ec739SDaniel Rosenberg 	root->dev_offset = 0;
1376*508ec739SDaniel Rosenberg 	err = exfat_build_file_dentry_set(exfat, " ", ATTR_SUBDIR,
1377*508ec739SDaniel Rosenberg 					  &root->dentry_set, &root->dentry_count);
1378*508ec739SDaniel Rosenberg 	if (err) {
1379*508ec739SDaniel Rosenberg 		exfat_free_inode(root);
1380*508ec739SDaniel Rosenberg 		return -ENOMEM;
1381*508ec739SDaniel Rosenberg 	}
1382*508ec739SDaniel Rosenberg 	return 0;
1383*508ec739SDaniel Rosenberg }
1384*508ec739SDaniel Rosenberg 
read_lostfound(struct exfat * exfat,struct exfat_inode ** lostfound)1385*508ec739SDaniel Rosenberg static int read_lostfound(struct exfat *exfat, struct exfat_inode **lostfound)
1386*508ec739SDaniel Rosenberg {
1387*508ec739SDaniel Rosenberg 	struct exfat_lookup_filter filter;
1388*508ec739SDaniel Rosenberg 	struct exfat_inode *inode;
1389*508ec739SDaniel Rosenberg 	int err;
1390*508ec739SDaniel Rosenberg 
1391*508ec739SDaniel Rosenberg 	err = exfat_lookup_file(exfat, exfat->root, "LOST+FOUND", &filter);
1392*508ec739SDaniel Rosenberg 	if (err)
1393*508ec739SDaniel Rosenberg 		return err;
1394*508ec739SDaniel Rosenberg 
1395*508ec739SDaniel Rosenberg 	inode = exfat_alloc_inode(ATTR_SUBDIR);
1396*508ec739SDaniel Rosenberg 	if (!inode) {
1397*508ec739SDaniel Rosenberg 		free(filter.out.dentry_set);
1398*508ec739SDaniel Rosenberg 		return -ENOMEM;
1399*508ec739SDaniel Rosenberg 	}
1400*508ec739SDaniel Rosenberg 
1401*508ec739SDaniel Rosenberg 	inode->dentry_set = filter.out.dentry_set;
1402*508ec739SDaniel Rosenberg 	inode->dentry_count = filter.out.dentry_count;
1403*508ec739SDaniel Rosenberg 	inode->dev_offset = filter.out.dev_offset;
1404*508ec739SDaniel Rosenberg 
1405*508ec739SDaniel Rosenberg 	inode->first_clus =
1406*508ec739SDaniel Rosenberg 		le32_to_cpu(filter.out.dentry_set[1].dentry.stream.start_clu);
1407*508ec739SDaniel Rosenberg 	inode->size =
1408*508ec739SDaniel Rosenberg 		le64_to_cpu(filter.out.dentry_set[1].dentry.stream.size);
1409*508ec739SDaniel Rosenberg 
1410*508ec739SDaniel Rosenberg 	*lostfound = inode;
1411*508ec739SDaniel Rosenberg 	return 0;
1412*508ec739SDaniel Rosenberg }
1413*508ec739SDaniel Rosenberg 
1414*508ec739SDaniel Rosenberg /* Create temporary files under LOST+FOUND and assign orphan
1415*508ec739SDaniel Rosenberg  * chains of clusters to these files.
1416*508ec739SDaniel Rosenberg  */
rescue_orphan_clusters(struct exfat_fsck * fsck)1417*508ec739SDaniel Rosenberg static int rescue_orphan_clusters(struct exfat_fsck *fsck)
1418*508ec739SDaniel Rosenberg {
1419*508ec739SDaniel Rosenberg 	struct exfat *exfat = fsck->exfat;
1420*508ec739SDaniel Rosenberg 	struct exfat_inode *lostfound;
1421*508ec739SDaniel Rosenberg 	bitmap_t *disk_b, *alloc_b, *ohead_b;
1422*508ec739SDaniel Rosenberg 	struct exfat_dentry *dset;
1423*508ec739SDaniel Rosenberg 	clus_t clu_count, clu, s_clu, e_clu;
1424*508ec739SDaniel Rosenberg 	int err, dcount;
1425*508ec739SDaniel Rosenberg 	unsigned int i;
1426*508ec739SDaniel Rosenberg 	char name[] = "FILE0000000.CHK";
1427*508ec739SDaniel Rosenberg 	struct exfat_dentry_loc loc;
1428*508ec739SDaniel Rosenberg 	struct exfat_lookup_filter lf = {
1429*508ec739SDaniel Rosenberg 		.in.type = EXFAT_INVAL,
1430*508ec739SDaniel Rosenberg 		.in.filter = NULL,
1431*508ec739SDaniel Rosenberg 	};
1432*508ec739SDaniel Rosenberg 
1433*508ec739SDaniel Rosenberg 	err = read_lostfound(exfat, &lostfound);
1434*508ec739SDaniel Rosenberg 	if (err) {
1435*508ec739SDaniel Rosenberg 		exfat_err("failed to find LOST+FOUND\n");
1436*508ec739SDaniel Rosenberg 		return err;
1437*508ec739SDaniel Rosenberg 	}
1438*508ec739SDaniel Rosenberg 
1439*508ec739SDaniel Rosenberg 	/* get the last empty region of LOST+FOUND */
1440*508ec739SDaniel Rosenberg 	err = exfat_lookup_dentry_set(exfat, lostfound, &lf);
1441*508ec739SDaniel Rosenberg 	if (err && err != EOF) {
1442*508ec739SDaniel Rosenberg 		exfat_err("failed to find the last empty slot in LOST+FOUND\n");
1443*508ec739SDaniel Rosenberg 		goto out;
1444*508ec739SDaniel Rosenberg 	}
1445*508ec739SDaniel Rosenberg 
1446*508ec739SDaniel Rosenberg 	loc.parent = lostfound;
1447*508ec739SDaniel Rosenberg 	loc.file_offset = lf.out.file_offset;
1448*508ec739SDaniel Rosenberg 	loc.dev_offset = lf.out.dev_offset;
1449*508ec739SDaniel Rosenberg 
1450*508ec739SDaniel Rosenberg 	/* build a template dentry set */
1451*508ec739SDaniel Rosenberg 	err = exfat_build_file_dentry_set(exfat, name, 0, &dset, &dcount);
1452*508ec739SDaniel Rosenberg 	if (err) {
1453*508ec739SDaniel Rosenberg 		exfat_err("failed to create a temporary file in LOST+FOUNDn");
1454*508ec739SDaniel Rosenberg 		goto out;
1455*508ec739SDaniel Rosenberg 	}
1456*508ec739SDaniel Rosenberg 	dset[1].dentry.stream.flags |= EXFAT_SF_CONTIGUOUS;
1457*508ec739SDaniel Rosenberg 
1458*508ec739SDaniel Rosenberg 	clu_count = le32_to_cpu(exfat->bs->bsx.clu_count);
1459*508ec739SDaniel Rosenberg 
1460*508ec739SDaniel Rosenberg 	/* find clusters which are not marked as free, but not allocated to
1461*508ec739SDaniel Rosenberg 	 * any files.
1462*508ec739SDaniel Rosenberg 	 */
1463*508ec739SDaniel Rosenberg 	disk_b = (bitmap_t *)exfat->disk_bitmap;
1464*508ec739SDaniel Rosenberg 	alloc_b = (bitmap_t *)exfat->alloc_bitmap;
1465*508ec739SDaniel Rosenberg 	ohead_b = (bitmap_t *)exfat->ohead_bitmap;
1466*508ec739SDaniel Rosenberg 	for (i = 0; i < EXFAT_BITMAP_SIZE(clu_count) / sizeof(bitmap_t); i++)
1467*508ec739SDaniel Rosenberg 		ohead_b[i] = disk_b[i] & ~alloc_b[i];
1468*508ec739SDaniel Rosenberg 
1469*508ec739SDaniel Rosenberg 	/* create temporary files and allocate contiguous orphan clusters
1470*508ec739SDaniel Rosenberg 	 * to each file.
1471*508ec739SDaniel Rosenberg 	 */
1472*508ec739SDaniel Rosenberg 	for (clu = EXFAT_FIRST_CLUSTER; clu < clu_count + EXFAT_FIRST_CLUSTER &&
1473*508ec739SDaniel Rosenberg 	     exfat_bitmap_find_one(exfat, exfat->ohead_bitmap, clu, &s_clu) == 0;) {
1474*508ec739SDaniel Rosenberg 		if (exfat_bitmap_find_zero(exfat, exfat->ohead_bitmap, s_clu, &e_clu))
1475*508ec739SDaniel Rosenberg 			e_clu = clu_count + EXFAT_FIRST_CLUSTER;
1476*508ec739SDaniel Rosenberg 		clu = e_clu;
1477*508ec739SDaniel Rosenberg 
1478*508ec739SDaniel Rosenberg 		snprintf(name, sizeof(name), "FILE%07d.CHK",
1479*508ec739SDaniel Rosenberg 			 (unsigned int)(loc.file_offset >> 5));
1480*508ec739SDaniel Rosenberg 		err = exfat_update_file_dentry_set(exfat, dset, dcount,
1481*508ec739SDaniel Rosenberg 						   name, s_clu, e_clu - s_clu);
1482*508ec739SDaniel Rosenberg 		if (err)
1483*508ec739SDaniel Rosenberg 			continue;
1484*508ec739SDaniel Rosenberg 		err = exfat_add_dentry_set(exfat, &loc, dset, dcount, true);
1485*508ec739SDaniel Rosenberg 		if (err)
1486*508ec739SDaniel Rosenberg 			continue;
1487*508ec739SDaniel Rosenberg 	}
1488*508ec739SDaniel Rosenberg 
1489*508ec739SDaniel Rosenberg 	free(dset);
1490*508ec739SDaniel Rosenberg 	err = 0;
1491*508ec739SDaniel Rosenberg out:
1492*508ec739SDaniel Rosenberg 	exfat_free_inode(lostfound);
1493*508ec739SDaniel Rosenberg 	return err;
1494*508ec739SDaniel Rosenberg }
1495*508ec739SDaniel Rosenberg 
bytes_to_human_readable(size_t bytes)1496*508ec739SDaniel Rosenberg static char *bytes_to_human_readable(size_t bytes)
1497*508ec739SDaniel Rosenberg {
1498*508ec739SDaniel Rosenberg 	static const char * const units[] = {"B", "KB", "MB", "GB", "TB", "PB"};
1499*508ec739SDaniel Rosenberg 	static char buf[15*4];
1500*508ec739SDaniel Rosenberg 	unsigned int i, shift, quoti, remain;
1501*508ec739SDaniel Rosenberg 	i = sizeof(units) / sizeof(units[0]) - 1;
1502*508ec739SDaniel Rosenberg 
1503*508ec739SDaniel Rosenberg 	while (i && (bytes >> i * 10) == 0)
1504*508ec739SDaniel Rosenberg 		i--;
1505*508ec739SDaniel Rosenberg 
1506*508ec739SDaniel Rosenberg 	shift = i * 10;
1507*508ec739SDaniel Rosenberg 	quoti = (unsigned int)(bytes / (1ULL << shift));
1508*508ec739SDaniel Rosenberg 	remain = 0;
1509*508ec739SDaniel Rosenberg 	if (shift > 0) {
1510*508ec739SDaniel Rosenberg 		remain = (unsigned int)
1511*508ec739SDaniel Rosenberg 			((bytes & ((1ULL << shift) - 1)) >> (shift - 10));
1512*508ec739SDaniel Rosenberg 		remain = (remain * 100) / 1024;
1513*508ec739SDaniel Rosenberg 	}
1514*508ec739SDaniel Rosenberg 
1515*508ec739SDaniel Rosenberg 	snprintf(buf, sizeof(buf), "%u.%02u %s", quoti, remain, units[i]);
1516*508ec739SDaniel Rosenberg 	return buf;
1517*508ec739SDaniel Rosenberg }
1518*508ec739SDaniel Rosenberg 
exfat_show_info(struct exfat_fsck * fsck,const char * dev_name)1519*508ec739SDaniel Rosenberg static void exfat_show_info(struct exfat_fsck *fsck, const char *dev_name)
1520*508ec739SDaniel Rosenberg {
1521*508ec739SDaniel Rosenberg 	struct exfat *exfat = fsck->exfat;
1522*508ec739SDaniel Rosenberg 	bool clean;
1523*508ec739SDaniel Rosenberg 
1524*508ec739SDaniel Rosenberg 	exfat_info("sector size:  %s\n",
1525*508ec739SDaniel Rosenberg 		bytes_to_human_readable(1 << exfat->bs->bsx.sect_size_bits));
1526*508ec739SDaniel Rosenberg 	exfat_info("cluster size: %s\n",
1527*508ec739SDaniel Rosenberg 		bytes_to_human_readable(exfat->clus_size));
1528*508ec739SDaniel Rosenberg 	exfat_info("volume size:  %s\n",
1529*508ec739SDaniel Rosenberg 		bytes_to_human_readable(exfat->blk_dev->size));
1530*508ec739SDaniel Rosenberg 
1531*508ec739SDaniel Rosenberg 	clean = exfat_stat.error_count == 0 ||
1532*508ec739SDaniel Rosenberg 		exfat_stat.error_count == exfat_stat.fixed_count;
1533*508ec739SDaniel Rosenberg 	printf("%s: %s. directories %ld, files %ld\n", dev_name,
1534*508ec739SDaniel Rosenberg 			clean ? "clean" : "corrupted",
1535*508ec739SDaniel Rosenberg 			exfat_stat.dir_count, exfat_stat.file_count);
1536*508ec739SDaniel Rosenberg 	if (exfat_stat.error_count)
1537*508ec739SDaniel Rosenberg 		printf("%s: files corrupted %ld, files fixed %ld\n", dev_name,
1538*508ec739SDaniel Rosenberg 			exfat_stat.error_count - exfat_stat.fixed_count,
1539*508ec739SDaniel Rosenberg 			exfat_stat.fixed_count);
1540*508ec739SDaniel Rosenberg }
1541*508ec739SDaniel Rosenberg 
main(int argc,char * const argv[])1542*508ec739SDaniel Rosenberg int main(int argc, char * const argv[])
1543*508ec739SDaniel Rosenberg {
1544*508ec739SDaniel Rosenberg 	struct fsck_user_input ui;
1545*508ec739SDaniel Rosenberg 	struct exfat_blk_dev bd;
1546*508ec739SDaniel Rosenberg 	struct pbr *bs = NULL;
1547*508ec739SDaniel Rosenberg 	int c, ret, exit_code;
1548*508ec739SDaniel Rosenberg 	bool version_only = false;
1549*508ec739SDaniel Rosenberg 
1550*508ec739SDaniel Rosenberg 	memset(&ui, 0, sizeof(ui));
1551*508ec739SDaniel Rosenberg 	memset(&bd, 0, sizeof(bd));
1552*508ec739SDaniel Rosenberg 
1553*508ec739SDaniel Rosenberg 	print_level = EXFAT_ERROR;
1554*508ec739SDaniel Rosenberg 
1555*508ec739SDaniel Rosenberg 	if (!setlocale(LC_CTYPE, ""))
1556*508ec739SDaniel Rosenberg 		exfat_err("failed to init locale/codeset\n");
1557*508ec739SDaniel Rosenberg 
1558*508ec739SDaniel Rosenberg 	opterr = 0;
1559*508ec739SDaniel Rosenberg 	while ((c = getopt_long(argc, argv, "arynpbsVvh", opts, NULL)) != EOF) {
1560*508ec739SDaniel Rosenberg 		switch (c) {
1561*508ec739SDaniel Rosenberg 		case 'n':
1562*508ec739SDaniel Rosenberg 			if (ui.options & FSCK_OPTS_REPAIR_ALL)
1563*508ec739SDaniel Rosenberg 				usage(argv[0]);
1564*508ec739SDaniel Rosenberg 			ui.options |= FSCK_OPTS_REPAIR_NO;
1565*508ec739SDaniel Rosenberg 			break;
1566*508ec739SDaniel Rosenberg 		case 'r':
1567*508ec739SDaniel Rosenberg 			if (ui.options & FSCK_OPTS_REPAIR_ALL)
1568*508ec739SDaniel Rosenberg 				usage(argv[0]);
1569*508ec739SDaniel Rosenberg 			ui.options |= FSCK_OPTS_REPAIR_ASK;
1570*508ec739SDaniel Rosenberg 			break;
1571*508ec739SDaniel Rosenberg 		case 'y':
1572*508ec739SDaniel Rosenberg 			if (ui.options & FSCK_OPTS_REPAIR_ALL)
1573*508ec739SDaniel Rosenberg 				usage(argv[0]);
1574*508ec739SDaniel Rosenberg 			ui.options |= FSCK_OPTS_REPAIR_YES;
1575*508ec739SDaniel Rosenberg 			break;
1576*508ec739SDaniel Rosenberg 		case 'a':
1577*508ec739SDaniel Rosenberg 		case 'p':
1578*508ec739SDaniel Rosenberg 			if (ui.options & FSCK_OPTS_REPAIR_ALL)
1579*508ec739SDaniel Rosenberg 				usage(argv[0]);
1580*508ec739SDaniel Rosenberg 			ui.options |= FSCK_OPTS_REPAIR_AUTO;
1581*508ec739SDaniel Rosenberg 			break;
1582*508ec739SDaniel Rosenberg 		case 'b':
1583*508ec739SDaniel Rosenberg 			ui.options |= FSCK_OPTS_IGNORE_BAD_FS_NAME;
1584*508ec739SDaniel Rosenberg 			break;
1585*508ec739SDaniel Rosenberg 		case 's':
1586*508ec739SDaniel Rosenberg 			ui.options |= FSCK_OPTS_RESCUE_CLUS;
1587*508ec739SDaniel Rosenberg 			break;
1588*508ec739SDaniel Rosenberg 		case 'V':
1589*508ec739SDaniel Rosenberg 			version_only = true;
1590*508ec739SDaniel Rosenberg 			break;
1591*508ec739SDaniel Rosenberg 		case 'v':
1592*508ec739SDaniel Rosenberg 			if (print_level < EXFAT_DEBUG)
1593*508ec739SDaniel Rosenberg 				print_level++;
1594*508ec739SDaniel Rosenberg 			break;
1595*508ec739SDaniel Rosenberg 		case '?':
1596*508ec739SDaniel Rosenberg 		case 'h':
1597*508ec739SDaniel Rosenberg 		default:
1598*508ec739SDaniel Rosenberg 			usage(argv[0]);
1599*508ec739SDaniel Rosenberg 		}
1600*508ec739SDaniel Rosenberg 	}
1601*508ec739SDaniel Rosenberg 
1602*508ec739SDaniel Rosenberg 	show_version();
1603*508ec739SDaniel Rosenberg 	if (optind != argc - 1)
1604*508ec739SDaniel Rosenberg 		usage(argv[0]);
1605*508ec739SDaniel Rosenberg 
1606*508ec739SDaniel Rosenberg 	if (version_only)
1607*508ec739SDaniel Rosenberg 		exit(FSCK_EXIT_SYNTAX_ERROR);
1608*508ec739SDaniel Rosenberg 	if (ui.options & FSCK_OPTS_REPAIR_WRITE)
1609*508ec739SDaniel Rosenberg 		ui.ei.writeable = true;
1610*508ec739SDaniel Rosenberg 	else {
1611*508ec739SDaniel Rosenberg 		if (ui.options & (FSCK_OPTS_IGNORE_BAD_FS_NAME |
1612*508ec739SDaniel Rosenberg 				  FSCK_OPTS_RESCUE_CLUS))
1613*508ec739SDaniel Rosenberg 			usage(argv[0]);
1614*508ec739SDaniel Rosenberg 		ui.options |= FSCK_OPTS_REPAIR_NO;
1615*508ec739SDaniel Rosenberg 		ui.ei.writeable = false;
1616*508ec739SDaniel Rosenberg 	}
1617*508ec739SDaniel Rosenberg 
1618*508ec739SDaniel Rosenberg 	exfat_fsck.options = ui.options;
1619*508ec739SDaniel Rosenberg 
1620*508ec739SDaniel Rosenberg 	snprintf(ui.ei.dev_name, sizeof(ui.ei.dev_name), "%s", argv[optind]);
1621*508ec739SDaniel Rosenberg 	ret = exfat_get_blk_dev_info(&ui.ei, &bd);
1622*508ec739SDaniel Rosenberg 	if (ret < 0) {
1623*508ec739SDaniel Rosenberg 		exfat_err("failed to open %s. %d\n", ui.ei.dev_name, ret);
1624*508ec739SDaniel Rosenberg 		return FSCK_EXIT_OPERATION_ERROR;
1625*508ec739SDaniel Rosenberg 	}
1626*508ec739SDaniel Rosenberg 
1627*508ec739SDaniel Rosenberg 	ret = exfat_boot_region_check(&bd, &bs,
1628*508ec739SDaniel Rosenberg 				      ui.options & FSCK_OPTS_IGNORE_BAD_FS_NAME ?
1629*508ec739SDaniel Rosenberg 				      true : false);
1630*508ec739SDaniel Rosenberg 	if (ret)
1631*508ec739SDaniel Rosenberg 		goto err;
1632*508ec739SDaniel Rosenberg 
1633*508ec739SDaniel Rosenberg 	exfat_fsck.exfat = exfat_alloc_exfat(&bd, bs);
1634*508ec739SDaniel Rosenberg 	if (!exfat_fsck.exfat) {
1635*508ec739SDaniel Rosenberg 		ret = -ENOMEM;
1636*508ec739SDaniel Rosenberg 		goto err;
1637*508ec739SDaniel Rosenberg 	}
1638*508ec739SDaniel Rosenberg 
1639*508ec739SDaniel Rosenberg 	exfat_fsck.buffer_desc = exfat_alloc_buffer(2,
1640*508ec739SDaniel Rosenberg 						    exfat_fsck.exfat->clus_size,
1641*508ec739SDaniel Rosenberg 						    exfat_fsck.exfat->sect_size);
1642*508ec739SDaniel Rosenberg 	if (!exfat_fsck.buffer_desc) {
1643*508ec739SDaniel Rosenberg 		ret = -ENOMEM;
1644*508ec739SDaniel Rosenberg 		goto err;
1645*508ec739SDaniel Rosenberg 	}
1646*508ec739SDaniel Rosenberg 
1647*508ec739SDaniel Rosenberg 	if ((exfat_fsck.options & FSCK_OPTS_REPAIR_WRITE) &&
1648*508ec739SDaniel Rosenberg 	    exfat_mark_volume_dirty(exfat_fsck.exfat, true)) {
1649*508ec739SDaniel Rosenberg 		ret = -EIO;
1650*508ec739SDaniel Rosenberg 		goto err;
1651*508ec739SDaniel Rosenberg 	}
1652*508ec739SDaniel Rosenberg 
1653*508ec739SDaniel Rosenberg 	exfat_debug("verifying root directory...\n");
1654*508ec739SDaniel Rosenberg 	ret = exfat_root_dir_check(exfat_fsck.exfat);
1655*508ec739SDaniel Rosenberg 	if (ret) {
1656*508ec739SDaniel Rosenberg 		exfat_err("failed to verify root directory.\n");
1657*508ec739SDaniel Rosenberg 		goto out;
1658*508ec739SDaniel Rosenberg 	}
1659*508ec739SDaniel Rosenberg 
1660*508ec739SDaniel Rosenberg 	if (exfat_fsck.options & FSCK_OPTS_RESCUE_CLUS) {
1661*508ec739SDaniel Rosenberg 		ret = exfat_create_file(exfat_fsck.exfat,
1662*508ec739SDaniel Rosenberg 					exfat_fsck.exfat->root,
1663*508ec739SDaniel Rosenberg 					"LOST+FOUND",
1664*508ec739SDaniel Rosenberg 					ATTR_SUBDIR);
1665*508ec739SDaniel Rosenberg 		if (ret) {
1666*508ec739SDaniel Rosenberg 			exfat_err("failed to create lost+found directory\n");
1667*508ec739SDaniel Rosenberg 			goto out;
1668*508ec739SDaniel Rosenberg 		}
1669*508ec739SDaniel Rosenberg 
1670*508ec739SDaniel Rosenberg 		if (fsync(exfat_fsck.exfat->blk_dev->dev_fd) != 0) {
1671*508ec739SDaniel Rosenberg 			ret = -EIO;
1672*508ec739SDaniel Rosenberg 			exfat_err("failed to sync()\n");
1673*508ec739SDaniel Rosenberg 			goto out;
1674*508ec739SDaniel Rosenberg 		}
1675*508ec739SDaniel Rosenberg 	}
1676*508ec739SDaniel Rosenberg 
1677*508ec739SDaniel Rosenberg 	exfat_debug("verifying directory entries...\n");
1678*508ec739SDaniel Rosenberg 	ret = exfat_filesystem_check(&exfat_fsck);
1679*508ec739SDaniel Rosenberg 	if (ret)
1680*508ec739SDaniel Rosenberg 		goto out;
1681*508ec739SDaniel Rosenberg 
1682*508ec739SDaniel Rosenberg 	if (exfat_fsck.options & FSCK_OPTS_RESCUE_CLUS) {
1683*508ec739SDaniel Rosenberg 		rescue_orphan_clusters(&exfat_fsck);
1684*508ec739SDaniel Rosenberg 		exfat_fsck.dirty = true;
1685*508ec739SDaniel Rosenberg 		exfat_fsck.dirty_fat = true;
1686*508ec739SDaniel Rosenberg 	}
1687*508ec739SDaniel Rosenberg 
1688*508ec739SDaniel Rosenberg 	if (exfat_fsck.options & FSCK_OPTS_REPAIR_WRITE) {
1689*508ec739SDaniel Rosenberg 		ret = write_bitmap(&exfat_fsck);
1690*508ec739SDaniel Rosenberg 		if (ret) {
1691*508ec739SDaniel Rosenberg 			exfat_err("failed to write bitmap\n");
1692*508ec739SDaniel Rosenberg 			goto out;
1693*508ec739SDaniel Rosenberg 		}
1694*508ec739SDaniel Rosenberg 	}
1695*508ec739SDaniel Rosenberg 
1696*508ec739SDaniel Rosenberg 	if (ui.ei.writeable && fsync(bd.dev_fd)) {
1697*508ec739SDaniel Rosenberg 		exfat_err("failed to sync\n");
1698*508ec739SDaniel Rosenberg 		ret = -EIO;
1699*508ec739SDaniel Rosenberg 		goto out;
1700*508ec739SDaniel Rosenberg 	}
1701*508ec739SDaniel Rosenberg 	if (exfat_fsck.options & FSCK_OPTS_REPAIR_WRITE)
1702*508ec739SDaniel Rosenberg 		exfat_mark_volume_dirty(exfat_fsck.exfat, false);
1703*508ec739SDaniel Rosenberg 
1704*508ec739SDaniel Rosenberg out:
1705*508ec739SDaniel Rosenberg 	exfat_show_info(&exfat_fsck, ui.ei.dev_name);
1706*508ec739SDaniel Rosenberg err:
1707*508ec739SDaniel Rosenberg 	if (ret && ret != -EINVAL)
1708*508ec739SDaniel Rosenberg 		exit_code = FSCK_EXIT_OPERATION_ERROR;
1709*508ec739SDaniel Rosenberg 	else if (ret == -EINVAL ||
1710*508ec739SDaniel Rosenberg 		 exfat_stat.error_count != exfat_stat.fixed_count)
1711*508ec739SDaniel Rosenberg 		exit_code = FSCK_EXIT_ERRORS_LEFT;
1712*508ec739SDaniel Rosenberg 	else if (exfat_fsck.dirty)
1713*508ec739SDaniel Rosenberg 		exit_code = FSCK_EXIT_CORRECTED;
1714*508ec739SDaniel Rosenberg 	else
1715*508ec739SDaniel Rosenberg 		exit_code = FSCK_EXIT_NO_ERRORS;
1716*508ec739SDaniel Rosenberg 
1717*508ec739SDaniel Rosenberg 	if (exfat_fsck.buffer_desc)
1718*508ec739SDaniel Rosenberg 		exfat_free_buffer(exfat_fsck.buffer_desc, 2);
1719*508ec739SDaniel Rosenberg 	if (exfat_fsck.exfat)
1720*508ec739SDaniel Rosenberg 		exfat_free_exfat(exfat_fsck.exfat);
1721*508ec739SDaniel Rosenberg 	close(bd.dev_fd);
1722*508ec739SDaniel Rosenberg 	return exit_code;
1723*508ec739SDaniel Rosenberg }
1724