xref: /aosp_15_r20/external/exfatprogs/lib/exfat_dir.c (revision 508ec739de867a7549a0b8584942a00612dc5f1c)
1*508ec739SDaniel Rosenberg // SPDX-License-Identifier: GPL-2.0-or-later
2*508ec739SDaniel Rosenberg /*
3*508ec739SDaniel Rosenberg  *   Copyright (C) 2021 LG Electronics.
4*508ec739SDaniel Rosenberg  *
5*508ec739SDaniel Rosenberg  *   Author(s): Hyunchul Lee <[email protected]>
6*508ec739SDaniel Rosenberg  */
7*508ec739SDaniel Rosenberg #include <stdlib.h>
8*508ec739SDaniel Rosenberg #include <stdio.h>
9*508ec739SDaniel Rosenberg #include <errno.h>
10*508ec739SDaniel Rosenberg #include <fcntl.h>
11*508ec739SDaniel Rosenberg #include <string.h>
12*508ec739SDaniel Rosenberg #include <time.h>
13*508ec739SDaniel Rosenberg 
14*508ec739SDaniel Rosenberg #include "exfat_ondisk.h"
15*508ec739SDaniel Rosenberg #include "libexfat.h"
16*508ec739SDaniel Rosenberg #include "exfat_fs.h"
17*508ec739SDaniel Rosenberg #include "exfat_dir.h"
18*508ec739SDaniel Rosenberg 
19*508ec739SDaniel Rosenberg static struct path_resolve_ctx path_resolve_ctx;
20*508ec739SDaniel Rosenberg 
21*508ec739SDaniel Rosenberg #define fsck_err(parent, inode, fmt, ...)		\
22*508ec739SDaniel Rosenberg ({							\
23*508ec739SDaniel Rosenberg 		exfat_resolve_path_parent(&path_resolve_ctx,	\
24*508ec739SDaniel Rosenberg 			parent, inode);			\
25*508ec739SDaniel Rosenberg 		exfat_err("ERROR: %s: " fmt,		\
26*508ec739SDaniel Rosenberg 			path_resolve_ctx.local_path,	\
27*508ec739SDaniel Rosenberg 			##__VA_ARGS__);			\
28*508ec739SDaniel Rosenberg })
29*508ec739SDaniel Rosenberg 
write_block(struct exfat_de_iter * iter,unsigned int block)30*508ec739SDaniel Rosenberg static ssize_t write_block(struct exfat_de_iter *iter, unsigned int block)
31*508ec739SDaniel Rosenberg {
32*508ec739SDaniel Rosenberg 	off_t device_offset;
33*508ec739SDaniel Rosenberg 	struct exfat *exfat = iter->exfat;
34*508ec739SDaniel Rosenberg 	struct buffer_desc *desc;
35*508ec739SDaniel Rosenberg 	unsigned int i;
36*508ec739SDaniel Rosenberg 
37*508ec739SDaniel Rosenberg 	desc = &iter->buffer_desc[block & 0x01];
38*508ec739SDaniel Rosenberg 
39*508ec739SDaniel Rosenberg 	for (i = 0; i < iter->read_size / iter->write_size; i++) {
40*508ec739SDaniel Rosenberg 		if (desc->dirty[i]) {
41*508ec739SDaniel Rosenberg 			device_offset = exfat_c2o(exfat, desc->p_clus) +
42*508ec739SDaniel Rosenberg 				desc->offset;
43*508ec739SDaniel Rosenberg 			if (exfat_write(exfat->blk_dev->dev_fd,
44*508ec739SDaniel Rosenberg 					desc->buffer + i * iter->write_size,
45*508ec739SDaniel Rosenberg 					iter->write_size,
46*508ec739SDaniel Rosenberg 					device_offset + i * iter->write_size)
47*508ec739SDaniel Rosenberg 					!= (ssize_t)iter->write_size)
48*508ec739SDaniel Rosenberg 				return -EIO;
49*508ec739SDaniel Rosenberg 			desc->dirty[i] = 0;
50*508ec739SDaniel Rosenberg 		}
51*508ec739SDaniel Rosenberg 	}
52*508ec739SDaniel Rosenberg 	return 0;
53*508ec739SDaniel Rosenberg }
54*508ec739SDaniel Rosenberg 
read_ahead_first_blocks(struct exfat_de_iter * iter)55*508ec739SDaniel Rosenberg static int read_ahead_first_blocks(struct exfat_de_iter *iter)
56*508ec739SDaniel Rosenberg {
57*508ec739SDaniel Rosenberg #ifdef POSIX_FADV_WILLNEED
58*508ec739SDaniel Rosenberg 	struct exfat *exfat = iter->exfat;
59*508ec739SDaniel Rosenberg 	clus_t clus_count;
60*508ec739SDaniel Rosenberg 	unsigned int size;
61*508ec739SDaniel Rosenberg 
62*508ec739SDaniel Rosenberg 	clus_count = iter->parent->size / exfat->clus_size;
63*508ec739SDaniel Rosenberg 
64*508ec739SDaniel Rosenberg 	if (clus_count > 1) {
65*508ec739SDaniel Rosenberg 		iter->ra_begin_offset = 0;
66*508ec739SDaniel Rosenberg 		iter->ra_next_clus = 1;
67*508ec739SDaniel Rosenberg 		size = exfat->clus_size;
68*508ec739SDaniel Rosenberg 	} else {
69*508ec739SDaniel Rosenberg 		iter->ra_begin_offset = 0;
70*508ec739SDaniel Rosenberg 		iter->ra_next_clus = 0;
71*508ec739SDaniel Rosenberg 		size = iter->ra_partial_size;
72*508ec739SDaniel Rosenberg 	}
73*508ec739SDaniel Rosenberg 	return posix_fadvise(exfat->blk_dev->dev_fd,
74*508ec739SDaniel Rosenberg 			exfat_c2o(exfat, iter->parent->first_clus), size,
75*508ec739SDaniel Rosenberg 			POSIX_FADV_WILLNEED);
76*508ec739SDaniel Rosenberg #else
77*508ec739SDaniel Rosenberg 	return -ENOTSUP;
78*508ec739SDaniel Rosenberg #endif
79*508ec739SDaniel Rosenberg }
80*508ec739SDaniel Rosenberg 
81*508ec739SDaniel Rosenberg /**
82*508ec739SDaniel Rosenberg  * read the next fragment in advance, and assume the fragment
83*508ec739SDaniel Rosenberg  * which covers @clus is already read.
84*508ec739SDaniel Rosenberg  */
read_ahead_next_blocks(struct exfat_de_iter * iter,clus_t clus,unsigned int offset,clus_t p_clus)85*508ec739SDaniel Rosenberg static int read_ahead_next_blocks(struct exfat_de_iter *iter,
86*508ec739SDaniel Rosenberg 		clus_t clus, unsigned int offset, clus_t p_clus)
87*508ec739SDaniel Rosenberg {
88*508ec739SDaniel Rosenberg #ifdef POSIX_FADV_WILLNEED
89*508ec739SDaniel Rosenberg 	struct exfat *exfat = iter->exfat;
90*508ec739SDaniel Rosenberg 	off_t device_offset;
91*508ec739SDaniel Rosenberg 	clus_t clus_count, ra_clus, ra_p_clus;
92*508ec739SDaniel Rosenberg 	unsigned int size;
93*508ec739SDaniel Rosenberg 	int ret = 0;
94*508ec739SDaniel Rosenberg 
95*508ec739SDaniel Rosenberg 	clus_count = iter->parent->size / exfat->clus_size;
96*508ec739SDaniel Rosenberg 	if (clus + 1 < clus_count) {
97*508ec739SDaniel Rosenberg 		ra_clus = clus + 1;
98*508ec739SDaniel Rosenberg 		if (ra_clus == iter->ra_next_clus &&
99*508ec739SDaniel Rosenberg 				offset >= iter->ra_begin_offset) {
100*508ec739SDaniel Rosenberg 			ret = exfat_get_inode_next_clus(exfat, iter->parent,
101*508ec739SDaniel Rosenberg 							p_clus, &ra_p_clus);
102*508ec739SDaniel Rosenberg 			if (ret)
103*508ec739SDaniel Rosenberg 				return ret;
104*508ec739SDaniel Rosenberg 
105*508ec739SDaniel Rosenberg 			if (ra_p_clus == EXFAT_EOF_CLUSTER)
106*508ec739SDaniel Rosenberg 				return -EIO;
107*508ec739SDaniel Rosenberg 
108*508ec739SDaniel Rosenberg 			device_offset = exfat_c2o(exfat, ra_p_clus);
109*508ec739SDaniel Rosenberg 			size = ra_clus + 1 < clus_count ?
110*508ec739SDaniel Rosenberg 				exfat->clus_size : iter->ra_partial_size;
111*508ec739SDaniel Rosenberg 			ret = posix_fadvise(exfat->blk_dev->dev_fd,
112*508ec739SDaniel Rosenberg 					device_offset, size,
113*508ec739SDaniel Rosenberg 					POSIX_FADV_WILLNEED);
114*508ec739SDaniel Rosenberg 			iter->ra_next_clus = ra_clus + 1;
115*508ec739SDaniel Rosenberg 			iter->ra_begin_offset = 0;
116*508ec739SDaniel Rosenberg 		}
117*508ec739SDaniel Rosenberg 	} else {
118*508ec739SDaniel Rosenberg 		if (offset >= iter->ra_begin_offset &&
119*508ec739SDaniel Rosenberg 				offset + iter->ra_partial_size <=
120*508ec739SDaniel Rosenberg 				exfat->clus_size) {
121*508ec739SDaniel Rosenberg 			device_offset = exfat_c2o(exfat, p_clus) +
122*508ec739SDaniel Rosenberg 				offset + iter->ra_partial_size;
123*508ec739SDaniel Rosenberg 			ret = posix_fadvise(exfat->blk_dev->dev_fd,
124*508ec739SDaniel Rosenberg 					device_offset, iter->ra_partial_size,
125*508ec739SDaniel Rosenberg 					POSIX_FADV_WILLNEED);
126*508ec739SDaniel Rosenberg 			iter->ra_begin_offset =
127*508ec739SDaniel Rosenberg 				offset + iter->ra_partial_size;
128*508ec739SDaniel Rosenberg 		}
129*508ec739SDaniel Rosenberg 	}
130*508ec739SDaniel Rosenberg 
131*508ec739SDaniel Rosenberg 	return ret;
132*508ec739SDaniel Rosenberg #else
133*508ec739SDaniel Rosenberg 	return -ENOTSUP;
134*508ec739SDaniel Rosenberg #endif
135*508ec739SDaniel Rosenberg }
136*508ec739SDaniel Rosenberg 
read_ahead_next_dir_blocks(struct exfat_de_iter * iter)137*508ec739SDaniel Rosenberg static int read_ahead_next_dir_blocks(struct exfat_de_iter *iter)
138*508ec739SDaniel Rosenberg {
139*508ec739SDaniel Rosenberg #ifdef POSIX_FADV_WILLNEED
140*508ec739SDaniel Rosenberg 	struct exfat *exfat = iter->exfat;
141*508ec739SDaniel Rosenberg 	struct list_head *current;
142*508ec739SDaniel Rosenberg 	struct exfat_inode *next_inode;
143*508ec739SDaniel Rosenberg 	off_t offset;
144*508ec739SDaniel Rosenberg 
145*508ec739SDaniel Rosenberg 	if (list_empty(&exfat->dir_list))
146*508ec739SDaniel Rosenberg 		return -EINVAL;
147*508ec739SDaniel Rosenberg 
148*508ec739SDaniel Rosenberg 	current = exfat->dir_list.next;
149*508ec739SDaniel Rosenberg 	if (iter->parent == list_entry(current, struct exfat_inode, list) &&
150*508ec739SDaniel Rosenberg 			current->next != &exfat->dir_list) {
151*508ec739SDaniel Rosenberg 		next_inode = list_entry(current->next, struct exfat_inode,
152*508ec739SDaniel Rosenberg 				list);
153*508ec739SDaniel Rosenberg 		offset = exfat_c2o(exfat, next_inode->first_clus);
154*508ec739SDaniel Rosenberg 		return posix_fadvise(exfat->blk_dev->dev_fd, offset,
155*508ec739SDaniel Rosenberg 				iter->ra_partial_size,
156*508ec739SDaniel Rosenberg 				POSIX_FADV_WILLNEED);
157*508ec739SDaniel Rosenberg 	}
158*508ec739SDaniel Rosenberg 
159*508ec739SDaniel Rosenberg 	return 0;
160*508ec739SDaniel Rosenberg #else
161*508ec739SDaniel Rosenberg 	return -ENOTSUP;
162*508ec739SDaniel Rosenberg #endif
163*508ec739SDaniel Rosenberg }
164*508ec739SDaniel Rosenberg 
read_block(struct exfat_de_iter * iter,unsigned int block)165*508ec739SDaniel Rosenberg static ssize_t read_block(struct exfat_de_iter *iter, unsigned int block)
166*508ec739SDaniel Rosenberg {
167*508ec739SDaniel Rosenberg 	struct exfat *exfat = iter->exfat;
168*508ec739SDaniel Rosenberg 	struct buffer_desc *desc, *prev_desc;
169*508ec739SDaniel Rosenberg 	off_t device_offset;
170*508ec739SDaniel Rosenberg 	ssize_t ret;
171*508ec739SDaniel Rosenberg 
172*508ec739SDaniel Rosenberg 	desc = &iter->buffer_desc[block & 0x01];
173*508ec739SDaniel Rosenberg 	if (block == 0) {
174*508ec739SDaniel Rosenberg 		desc->p_clus = iter->parent->first_clus;
175*508ec739SDaniel Rosenberg 		desc->offset = 0;
176*508ec739SDaniel Rosenberg 	}
177*508ec739SDaniel Rosenberg 
178*508ec739SDaniel Rosenberg 	/* if the buffer already contains dirty dentries, write it */
179*508ec739SDaniel Rosenberg 	if (write_block(iter, block))
180*508ec739SDaniel Rosenberg 		return -EIO;
181*508ec739SDaniel Rosenberg 
182*508ec739SDaniel Rosenberg 	if (block > 0) {
183*508ec739SDaniel Rosenberg 		if (block > iter->parent->size / iter->read_size)
184*508ec739SDaniel Rosenberg 			return EOF;
185*508ec739SDaniel Rosenberg 
186*508ec739SDaniel Rosenberg 		prev_desc = &iter->buffer_desc[(block-1) & 0x01];
187*508ec739SDaniel Rosenberg 		if (prev_desc->offset + 2 * iter->read_size <=
188*508ec739SDaniel Rosenberg 				exfat->clus_size) {
189*508ec739SDaniel Rosenberg 			desc->p_clus = prev_desc->p_clus;
190*508ec739SDaniel Rosenberg 			desc->offset = prev_desc->offset + iter->read_size;
191*508ec739SDaniel Rosenberg 		} else {
192*508ec739SDaniel Rosenberg 			ret = exfat_get_inode_next_clus(exfat, iter->parent,
193*508ec739SDaniel Rosenberg 							prev_desc->p_clus, &desc->p_clus);
194*508ec739SDaniel Rosenberg 			desc->offset = 0;
195*508ec739SDaniel Rosenberg 			if (ret)
196*508ec739SDaniel Rosenberg 				return ret;
197*508ec739SDaniel Rosenberg 			else if (desc->p_clus == EXFAT_EOF_CLUSTER)
198*508ec739SDaniel Rosenberg 				return EOF;
199*508ec739SDaniel Rosenberg 		}
200*508ec739SDaniel Rosenberg 	}
201*508ec739SDaniel Rosenberg 
202*508ec739SDaniel Rosenberg 	device_offset = exfat_c2o(exfat, desc->p_clus) + desc->offset;
203*508ec739SDaniel Rosenberg 	ret = exfat_read(exfat->blk_dev->dev_fd, desc->buffer,
204*508ec739SDaniel Rosenberg 			iter->read_size, device_offset);
205*508ec739SDaniel Rosenberg 	if (ret <= 0)
206*508ec739SDaniel Rosenberg 		return ret;
207*508ec739SDaniel Rosenberg 
208*508ec739SDaniel Rosenberg 	/*
209*508ec739SDaniel Rosenberg 	 * if a buffer is filled with dentries, read blocks ahead of time,
210*508ec739SDaniel Rosenberg 	 * otherwise read blocks of the next directory in advance.
211*508ec739SDaniel Rosenberg 	 */
212*508ec739SDaniel Rosenberg 	if (desc->buffer[iter->read_size - 32] != EXFAT_LAST)
213*508ec739SDaniel Rosenberg 		read_ahead_next_blocks(iter,
214*508ec739SDaniel Rosenberg 				(block * iter->read_size) / exfat->clus_size,
215*508ec739SDaniel Rosenberg 				(block * iter->read_size) % exfat->clus_size,
216*508ec739SDaniel Rosenberg 				desc->p_clus);
217*508ec739SDaniel Rosenberg 	else
218*508ec739SDaniel Rosenberg 		read_ahead_next_dir_blocks(iter);
219*508ec739SDaniel Rosenberg 	return ret;
220*508ec739SDaniel Rosenberg }
221*508ec739SDaniel Rosenberg 
exfat_de_iter_init(struct exfat_de_iter * iter,struct exfat * exfat,struct exfat_inode * dir,struct buffer_desc * bd)222*508ec739SDaniel Rosenberg int exfat_de_iter_init(struct exfat_de_iter *iter, struct exfat *exfat,
223*508ec739SDaniel Rosenberg 		       struct exfat_inode *dir, struct buffer_desc *bd)
224*508ec739SDaniel Rosenberg {
225*508ec739SDaniel Rosenberg 	iter->exfat = exfat;
226*508ec739SDaniel Rosenberg 	iter->parent = dir;
227*508ec739SDaniel Rosenberg 	iter->write_size = exfat->sect_size;
228*508ec739SDaniel Rosenberg 	iter->read_size = exfat->clus_size <= 4*KB ? exfat->clus_size : 4*KB;
229*508ec739SDaniel Rosenberg 	if (exfat->clus_size <= 32 * KB)
230*508ec739SDaniel Rosenberg 		iter->ra_partial_size = MAX(4 * KB, exfat->clus_size / 2);
231*508ec739SDaniel Rosenberg 	else
232*508ec739SDaniel Rosenberg 		iter->ra_partial_size = exfat->clus_size / 4;
233*508ec739SDaniel Rosenberg 	iter->ra_partial_size = MIN(iter->ra_partial_size, 8 * KB);
234*508ec739SDaniel Rosenberg 
235*508ec739SDaniel Rosenberg 	iter->buffer_desc = bd;
236*508ec739SDaniel Rosenberg 
237*508ec739SDaniel Rosenberg 	iter->de_file_offset = 0;
238*508ec739SDaniel Rosenberg 	iter->next_read_offset = iter->read_size;
239*508ec739SDaniel Rosenberg 	iter->max_skip_dentries = 0;
240*508ec739SDaniel Rosenberg 	iter->dot_name_num = 0;
241*508ec739SDaniel Rosenberg 
242*508ec739SDaniel Rosenberg 	if (iter->parent->size == 0)
243*508ec739SDaniel Rosenberg 		return EOF;
244*508ec739SDaniel Rosenberg 
245*508ec739SDaniel Rosenberg 	read_ahead_first_blocks(iter);
246*508ec739SDaniel Rosenberg 	if (read_block(iter, 0) != (ssize_t)iter->read_size) {
247*508ec739SDaniel Rosenberg 		exfat_err("failed to read directory entries.\n");
248*508ec739SDaniel Rosenberg 		return -EIO;
249*508ec739SDaniel Rosenberg 	}
250*508ec739SDaniel Rosenberg 
251*508ec739SDaniel Rosenberg 	return 0;
252*508ec739SDaniel Rosenberg }
253*508ec739SDaniel Rosenberg 
exfat_de_iter_get(struct exfat_de_iter * iter,int ith,struct exfat_dentry ** dentry)254*508ec739SDaniel Rosenberg int exfat_de_iter_get(struct exfat_de_iter *iter,
255*508ec739SDaniel Rosenberg 			int ith, struct exfat_dentry **dentry)
256*508ec739SDaniel Rosenberg {
257*508ec739SDaniel Rosenberg 	off_t next_de_file_offset;
258*508ec739SDaniel Rosenberg 	ssize_t ret;
259*508ec739SDaniel Rosenberg 	unsigned int block;
260*508ec739SDaniel Rosenberg 
261*508ec739SDaniel Rosenberg 	next_de_file_offset = iter->de_file_offset +
262*508ec739SDaniel Rosenberg 			ith * sizeof(struct exfat_dentry);
263*508ec739SDaniel Rosenberg 	block = (unsigned int)(next_de_file_offset / iter->read_size);
264*508ec739SDaniel Rosenberg 
265*508ec739SDaniel Rosenberg 	if (next_de_file_offset + sizeof(struct exfat_dentry) >
266*508ec739SDaniel Rosenberg 		iter->parent->size)
267*508ec739SDaniel Rosenberg 		return EOF;
268*508ec739SDaniel Rosenberg 	/* the dentry must be in current, or next block which will be read */
269*508ec739SDaniel Rosenberg 	if (block > iter->de_file_offset / iter->read_size + 1)
270*508ec739SDaniel Rosenberg 		return -ERANGE;
271*508ec739SDaniel Rosenberg 
272*508ec739SDaniel Rosenberg 	/* read next cluster if needed */
273*508ec739SDaniel Rosenberg 	if (next_de_file_offset >= iter->next_read_offset) {
274*508ec739SDaniel Rosenberg 		ret = read_block(iter, block);
275*508ec739SDaniel Rosenberg 		if (ret != (ssize_t)iter->read_size)
276*508ec739SDaniel Rosenberg 			return ret;
277*508ec739SDaniel Rosenberg 		iter->next_read_offset += iter->read_size;
278*508ec739SDaniel Rosenberg 	}
279*508ec739SDaniel Rosenberg 
280*508ec739SDaniel Rosenberg 	if (ith + 1 > iter->max_skip_dentries)
281*508ec739SDaniel Rosenberg 		iter->max_skip_dentries = ith + 1;
282*508ec739SDaniel Rosenberg 
283*508ec739SDaniel Rosenberg 	*dentry = (struct exfat_dentry *)
284*508ec739SDaniel Rosenberg 			(iter->buffer_desc[block & 0x01].buffer +
285*508ec739SDaniel Rosenberg 			next_de_file_offset % iter->read_size);
286*508ec739SDaniel Rosenberg 	return 0;
287*508ec739SDaniel Rosenberg }
288*508ec739SDaniel Rosenberg 
exfat_de_iter_get_dirty(struct exfat_de_iter * iter,int ith,struct exfat_dentry ** dentry)289*508ec739SDaniel Rosenberg int exfat_de_iter_get_dirty(struct exfat_de_iter *iter,
290*508ec739SDaniel Rosenberg 			int ith, struct exfat_dentry **dentry)
291*508ec739SDaniel Rosenberg {
292*508ec739SDaniel Rosenberg 	off_t next_file_offset;
293*508ec739SDaniel Rosenberg 	unsigned int block;
294*508ec739SDaniel Rosenberg 	int ret, sect_idx;
295*508ec739SDaniel Rosenberg 
296*508ec739SDaniel Rosenberg 	ret = exfat_de_iter_get(iter, ith, dentry);
297*508ec739SDaniel Rosenberg 	if (!ret) {
298*508ec739SDaniel Rosenberg 		next_file_offset = iter->de_file_offset +
299*508ec739SDaniel Rosenberg 				ith * sizeof(struct exfat_dentry);
300*508ec739SDaniel Rosenberg 		block = (unsigned int)(next_file_offset / iter->read_size);
301*508ec739SDaniel Rosenberg 		sect_idx = (int)((next_file_offset % iter->read_size) /
302*508ec739SDaniel Rosenberg 				iter->write_size);
303*508ec739SDaniel Rosenberg 		iter->buffer_desc[block & 0x01].dirty[sect_idx] = 1;
304*508ec739SDaniel Rosenberg 	}
305*508ec739SDaniel Rosenberg 
306*508ec739SDaniel Rosenberg 	return ret;
307*508ec739SDaniel Rosenberg }
308*508ec739SDaniel Rosenberg 
exfat_de_iter_flush(struct exfat_de_iter * iter)309*508ec739SDaniel Rosenberg int exfat_de_iter_flush(struct exfat_de_iter *iter)
310*508ec739SDaniel Rosenberg {
311*508ec739SDaniel Rosenberg 	if (write_block(iter, 0) || write_block(iter, 1))
312*508ec739SDaniel Rosenberg 		return -EIO;
313*508ec739SDaniel Rosenberg 	return 0;
314*508ec739SDaniel Rosenberg }
315*508ec739SDaniel Rosenberg 
exfat_de_iter_advance(struct exfat_de_iter * iter,int skip_dentries)316*508ec739SDaniel Rosenberg int exfat_de_iter_advance(struct exfat_de_iter *iter, int skip_dentries)
317*508ec739SDaniel Rosenberg {
318*508ec739SDaniel Rosenberg 	if (skip_dentries > iter->max_skip_dentries)
319*508ec739SDaniel Rosenberg 		return -EINVAL;
320*508ec739SDaniel Rosenberg 
321*508ec739SDaniel Rosenberg 	iter->max_skip_dentries = 0;
322*508ec739SDaniel Rosenberg 	iter->de_file_offset = iter->de_file_offset +
323*508ec739SDaniel Rosenberg 				skip_dentries * sizeof(struct exfat_dentry);
324*508ec739SDaniel Rosenberg 	return 0;
325*508ec739SDaniel Rosenberg }
326*508ec739SDaniel Rosenberg 
exfat_de_iter_device_offset(struct exfat_de_iter * iter)327*508ec739SDaniel Rosenberg off_t exfat_de_iter_device_offset(struct exfat_de_iter *iter)
328*508ec739SDaniel Rosenberg {
329*508ec739SDaniel Rosenberg 	struct buffer_desc *bd;
330*508ec739SDaniel Rosenberg 	unsigned int block;
331*508ec739SDaniel Rosenberg 
332*508ec739SDaniel Rosenberg 	if ((uint64_t)iter->de_file_offset >= iter->parent->size)
333*508ec739SDaniel Rosenberg 		return EOF;
334*508ec739SDaniel Rosenberg 
335*508ec739SDaniel Rosenberg 	block = iter->de_file_offset / iter->read_size;
336*508ec739SDaniel Rosenberg 	bd = &iter->buffer_desc[block & 0x01];
337*508ec739SDaniel Rosenberg 	return exfat_c2o(iter->exfat, bd->p_clus) + bd->offset +
338*508ec739SDaniel Rosenberg 		iter->de_file_offset % iter->read_size;
339*508ec739SDaniel Rosenberg }
340*508ec739SDaniel Rosenberg 
exfat_de_iter_file_offset(struct exfat_de_iter * iter)341*508ec739SDaniel Rosenberg off_t exfat_de_iter_file_offset(struct exfat_de_iter *iter)
342*508ec739SDaniel Rosenberg {
343*508ec739SDaniel Rosenberg 	return iter->de_file_offset;
344*508ec739SDaniel Rosenberg }
345*508ec739SDaniel Rosenberg 
346*508ec739SDaniel Rosenberg /*
347*508ec739SDaniel Rosenberg  * try to find the dentry set matched with @filter. this function
348*508ec739SDaniel Rosenberg  * doesn't verify the dentry set.
349*508ec739SDaniel Rosenberg  *
350*508ec739SDaniel Rosenberg  * if found, return 0. if not found, return EOF. otherwise return errno.
351*508ec739SDaniel Rosenberg  */
exfat_lookup_dentry_set(struct exfat * exfat,struct exfat_inode * parent,struct exfat_lookup_filter * filter)352*508ec739SDaniel Rosenberg int exfat_lookup_dentry_set(struct exfat *exfat, struct exfat_inode *parent,
353*508ec739SDaniel Rosenberg 			    struct exfat_lookup_filter *filter)
354*508ec739SDaniel Rosenberg {
355*508ec739SDaniel Rosenberg 	struct buffer_desc *bd = NULL;
356*508ec739SDaniel Rosenberg 	struct exfat_dentry *dentry = NULL;
357*508ec739SDaniel Rosenberg 	off_t free_file_offset = 0, free_dev_offset = 0;
358*508ec739SDaniel Rosenberg 	struct exfat_de_iter de_iter;
359*508ec739SDaniel Rosenberg 	int dentry_count;
360*508ec739SDaniel Rosenberg 	int retval;
361*508ec739SDaniel Rosenberg 	bool last_is_free = false;
362*508ec739SDaniel Rosenberg 
363*508ec739SDaniel Rosenberg 	bd = exfat_alloc_buffer(2, exfat->clus_size, exfat->sect_size);
364*508ec739SDaniel Rosenberg 	if (!bd)
365*508ec739SDaniel Rosenberg 		return -ENOMEM;
366*508ec739SDaniel Rosenberg 
367*508ec739SDaniel Rosenberg 	retval = exfat_de_iter_init(&de_iter, exfat, parent, bd);
368*508ec739SDaniel Rosenberg 	if (retval == EOF || retval)
369*508ec739SDaniel Rosenberg 		goto out;
370*508ec739SDaniel Rosenberg 
371*508ec739SDaniel Rosenberg 	filter->out.dentry_set = NULL;
372*508ec739SDaniel Rosenberg 	while (1) {
373*508ec739SDaniel Rosenberg 		retval = exfat_de_iter_get(&de_iter, 0, &dentry);
374*508ec739SDaniel Rosenberg 		if (retval == EOF) {
375*508ec739SDaniel Rosenberg 			break;
376*508ec739SDaniel Rosenberg 		} else if (retval) {
377*508ec739SDaniel Rosenberg 			fsck_err(parent->parent, parent,
378*508ec739SDaniel Rosenberg 				 "failed to get a dentry. %d\n", retval);
379*508ec739SDaniel Rosenberg 			goto out;
380*508ec739SDaniel Rosenberg 		}
381*508ec739SDaniel Rosenberg 
382*508ec739SDaniel Rosenberg 		dentry_count = 1;
383*508ec739SDaniel Rosenberg 		if (dentry->type == filter->in.type) {
384*508ec739SDaniel Rosenberg 			retval = 0;
385*508ec739SDaniel Rosenberg 			if (filter->in.filter)
386*508ec739SDaniel Rosenberg 				retval = filter->in.filter(&de_iter,
387*508ec739SDaniel Rosenberg 							filter->in.param,
388*508ec739SDaniel Rosenberg 							&dentry_count);
389*508ec739SDaniel Rosenberg 
390*508ec739SDaniel Rosenberg 			if (retval == 0) {
391*508ec739SDaniel Rosenberg 				struct exfat_dentry *d;
392*508ec739SDaniel Rosenberg 				int i;
393*508ec739SDaniel Rosenberg 
394*508ec739SDaniel Rosenberg 				filter->out.dentry_set = calloc(dentry_count,
395*508ec739SDaniel Rosenberg 								sizeof(struct exfat_dentry));
396*508ec739SDaniel Rosenberg 				if (!filter->out.dentry_set) {
397*508ec739SDaniel Rosenberg 					retval = -ENOMEM;
398*508ec739SDaniel Rosenberg 					goto out;
399*508ec739SDaniel Rosenberg 				}
400*508ec739SDaniel Rosenberg 				for (i = 0; i < dentry_count; i++) {
401*508ec739SDaniel Rosenberg 					exfat_de_iter_get(&de_iter, i, &d);
402*508ec739SDaniel Rosenberg 					memcpy(filter->out.dentry_set + i, d,
403*508ec739SDaniel Rosenberg 					       sizeof(struct exfat_dentry));
404*508ec739SDaniel Rosenberg 				}
405*508ec739SDaniel Rosenberg 				filter->out.dentry_count = dentry_count;
406*508ec739SDaniel Rosenberg 				goto out;
407*508ec739SDaniel Rosenberg 			} else if (retval < 0) {
408*508ec739SDaniel Rosenberg 				goto out;
409*508ec739SDaniel Rosenberg 			}
410*508ec739SDaniel Rosenberg 			last_is_free = false;
411*508ec739SDaniel Rosenberg 		} else if ((dentry->type == EXFAT_LAST ||
412*508ec739SDaniel Rosenberg 			    IS_EXFAT_DELETED(dentry->type))) {
413*508ec739SDaniel Rosenberg 			if (!last_is_free) {
414*508ec739SDaniel Rosenberg 				free_file_offset =
415*508ec739SDaniel Rosenberg 					exfat_de_iter_file_offset(&de_iter);
416*508ec739SDaniel Rosenberg 				free_dev_offset =
417*508ec739SDaniel Rosenberg 					exfat_de_iter_device_offset(&de_iter);
418*508ec739SDaniel Rosenberg 				last_is_free = true;
419*508ec739SDaniel Rosenberg 			}
420*508ec739SDaniel Rosenberg 		} else {
421*508ec739SDaniel Rosenberg 			last_is_free = false;
422*508ec739SDaniel Rosenberg 		}
423*508ec739SDaniel Rosenberg 
424*508ec739SDaniel Rosenberg 		exfat_de_iter_advance(&de_iter, dentry_count);
425*508ec739SDaniel Rosenberg 	}
426*508ec739SDaniel Rosenberg 
427*508ec739SDaniel Rosenberg out:
428*508ec739SDaniel Rosenberg 	if (retval == 0) {
429*508ec739SDaniel Rosenberg 		filter->out.file_offset =
430*508ec739SDaniel Rosenberg 			exfat_de_iter_file_offset(&de_iter);
431*508ec739SDaniel Rosenberg 		filter->out.dev_offset =
432*508ec739SDaniel Rosenberg 			exfat_de_iter_device_offset(&de_iter);
433*508ec739SDaniel Rosenberg 	} else if (retval == EOF && last_is_free) {
434*508ec739SDaniel Rosenberg 		filter->out.file_offset = free_file_offset;
435*508ec739SDaniel Rosenberg 		filter->out.dev_offset = free_dev_offset;
436*508ec739SDaniel Rosenberg 	} else {
437*508ec739SDaniel Rosenberg 		filter->out.file_offset = exfat_de_iter_file_offset(&de_iter);
438*508ec739SDaniel Rosenberg 		filter->out.dev_offset = EOF;
439*508ec739SDaniel Rosenberg 	}
440*508ec739SDaniel Rosenberg 	if (bd)
441*508ec739SDaniel Rosenberg 		exfat_free_buffer(bd, 2);
442*508ec739SDaniel Rosenberg 	return retval;
443*508ec739SDaniel Rosenberg }
444*508ec739SDaniel Rosenberg 
filter_lookup_file(struct exfat_de_iter * de_iter,void * param,int * dentry_count)445*508ec739SDaniel Rosenberg static int filter_lookup_file(struct exfat_de_iter *de_iter,
446*508ec739SDaniel Rosenberg 			      void *param, int *dentry_count)
447*508ec739SDaniel Rosenberg {
448*508ec739SDaniel Rosenberg 	struct exfat_dentry *file_de, *stream_de, *name_de;
449*508ec739SDaniel Rosenberg 	__le16 *name;
450*508ec739SDaniel Rosenberg 	int retval, name_len;
451*508ec739SDaniel Rosenberg 	int i;
452*508ec739SDaniel Rosenberg 
453*508ec739SDaniel Rosenberg 	retval = exfat_de_iter_get(de_iter, 0, &file_de);
454*508ec739SDaniel Rosenberg 	if (retval || file_de->type != EXFAT_FILE)
455*508ec739SDaniel Rosenberg 		return 1;
456*508ec739SDaniel Rosenberg 
457*508ec739SDaniel Rosenberg 	retval = exfat_de_iter_get(de_iter, 1, &stream_de);
458*508ec739SDaniel Rosenberg 	if (retval || stream_de->type != EXFAT_STREAM)
459*508ec739SDaniel Rosenberg 		return 1;
460*508ec739SDaniel Rosenberg 
461*508ec739SDaniel Rosenberg 	name = (__le16 *)param;
462*508ec739SDaniel Rosenberg 	name_len = (int)exfat_utf16_len(name, PATH_MAX);
463*508ec739SDaniel Rosenberg 
464*508ec739SDaniel Rosenberg 	if (file_de->dentry.file.num_ext <
465*508ec739SDaniel Rosenberg 		1 + (name_len + ENTRY_NAME_MAX - 1) / ENTRY_NAME_MAX)
466*508ec739SDaniel Rosenberg 		return 1;
467*508ec739SDaniel Rosenberg 
468*508ec739SDaniel Rosenberg 	for (i = 2; i <= file_de->dentry.file.num_ext && name_len > 0; i++) {
469*508ec739SDaniel Rosenberg 		int len;
470*508ec739SDaniel Rosenberg 
471*508ec739SDaniel Rosenberg 		retval = exfat_de_iter_get(de_iter, i, &name_de);
472*508ec739SDaniel Rosenberg 		if (retval || name_de->type != EXFAT_NAME)
473*508ec739SDaniel Rosenberg 			return 1;
474*508ec739SDaniel Rosenberg 
475*508ec739SDaniel Rosenberg 		len = MIN(name_len, ENTRY_NAME_MAX);
476*508ec739SDaniel Rosenberg 		if (memcmp(name_de->dentry.name.unicode_0_14,
477*508ec739SDaniel Rosenberg 			   name, len * 2) != 0)
478*508ec739SDaniel Rosenberg 			return 1;
479*508ec739SDaniel Rosenberg 
480*508ec739SDaniel Rosenberg 		name += len;
481*508ec739SDaniel Rosenberg 		name_len -= len;
482*508ec739SDaniel Rosenberg 	}
483*508ec739SDaniel Rosenberg 
484*508ec739SDaniel Rosenberg 	*dentry_count = i;
485*508ec739SDaniel Rosenberg 	return 0;
486*508ec739SDaniel Rosenberg }
487*508ec739SDaniel Rosenberg 
exfat_lookup_file(struct exfat * exfat,struct exfat_inode * parent,const char * name,struct exfat_lookup_filter * filter_out)488*508ec739SDaniel Rosenberg int exfat_lookup_file(struct exfat *exfat, struct exfat_inode *parent,
489*508ec739SDaniel Rosenberg 		      const char *name, struct exfat_lookup_filter *filter_out)
490*508ec739SDaniel Rosenberg {
491*508ec739SDaniel Rosenberg 	int retval;
492*508ec739SDaniel Rosenberg 	__le16 utf16_name[PATH_MAX + 2] = {0, };
493*508ec739SDaniel Rosenberg 
494*508ec739SDaniel Rosenberg 	retval = (int)exfat_utf16_enc(name, utf16_name, sizeof(utf16_name));
495*508ec739SDaniel Rosenberg 	if (retval < 0)
496*508ec739SDaniel Rosenberg 		return retval;
497*508ec739SDaniel Rosenberg 
498*508ec739SDaniel Rosenberg 	filter_out->in.type = EXFAT_FILE;
499*508ec739SDaniel Rosenberg 	filter_out->in.filter = filter_lookup_file;
500*508ec739SDaniel Rosenberg 	filter_out->in.param = utf16_name;
501*508ec739SDaniel Rosenberg 
502*508ec739SDaniel Rosenberg 	retval = exfat_lookup_dentry_set(exfat, parent, filter_out);
503*508ec739SDaniel Rosenberg 	if (retval < 0)
504*508ec739SDaniel Rosenberg 		return retval;
505*508ec739SDaniel Rosenberg 
506*508ec739SDaniel Rosenberg 	return 0;
507*508ec739SDaniel Rosenberg }
508*508ec739SDaniel Rosenberg 
exfat_calc_dentry_checksum(struct exfat_dentry * dentry,uint16_t * checksum,bool primary)509*508ec739SDaniel Rosenberg void exfat_calc_dentry_checksum(struct exfat_dentry *dentry,
510*508ec739SDaniel Rosenberg 				uint16_t *checksum, bool primary)
511*508ec739SDaniel Rosenberg {
512*508ec739SDaniel Rosenberg 	unsigned int i;
513*508ec739SDaniel Rosenberg 	uint8_t *bytes;
514*508ec739SDaniel Rosenberg 
515*508ec739SDaniel Rosenberg 	bytes = (uint8_t *)dentry;
516*508ec739SDaniel Rosenberg 
517*508ec739SDaniel Rosenberg 	*checksum = ((*checksum << 15) | (*checksum >> 1)) + bytes[0];
518*508ec739SDaniel Rosenberg 	*checksum = ((*checksum << 15) | (*checksum >> 1)) + bytes[1];
519*508ec739SDaniel Rosenberg 
520*508ec739SDaniel Rosenberg 	i = primary ? 4 : 2;
521*508ec739SDaniel Rosenberg 	for (; i < sizeof(*dentry); i++)
522*508ec739SDaniel Rosenberg 		*checksum = ((*checksum << 15) | (*checksum >> 1)) + bytes[i];
523*508ec739SDaniel Rosenberg }
524*508ec739SDaniel Rosenberg 
calc_dentry_set_checksum(struct exfat_dentry * dset,int dcount)525*508ec739SDaniel Rosenberg static uint16_t calc_dentry_set_checksum(struct exfat_dentry *dset, int dcount)
526*508ec739SDaniel Rosenberg {
527*508ec739SDaniel Rosenberg 	uint16_t checksum;
528*508ec739SDaniel Rosenberg 	int i;
529*508ec739SDaniel Rosenberg 
530*508ec739SDaniel Rosenberg 	if (dcount < MIN_FILE_DENTRIES)
531*508ec739SDaniel Rosenberg 		return 0;
532*508ec739SDaniel Rosenberg 
533*508ec739SDaniel Rosenberg 	checksum = 0;
534*508ec739SDaniel Rosenberg 	exfat_calc_dentry_checksum(&dset[0], &checksum, true);
535*508ec739SDaniel Rosenberg 	for (i = 1; i < dcount; i++)
536*508ec739SDaniel Rosenberg 		exfat_calc_dentry_checksum(&dset[i], &checksum, false);
537*508ec739SDaniel Rosenberg 	return checksum;
538*508ec739SDaniel Rosenberg }
539*508ec739SDaniel Rosenberg 
exfat_calc_name_hash(struct exfat * exfat,__le16 * name,int len)540*508ec739SDaniel Rosenberg uint16_t exfat_calc_name_hash(struct exfat *exfat,
541*508ec739SDaniel Rosenberg 			      __le16 *name, int len)
542*508ec739SDaniel Rosenberg {
543*508ec739SDaniel Rosenberg 	int i;
544*508ec739SDaniel Rosenberg 	__le16 ch;
545*508ec739SDaniel Rosenberg 	uint16_t chksum = 0;
546*508ec739SDaniel Rosenberg 
547*508ec739SDaniel Rosenberg 	for (i = 0; i < len; i++) {
548*508ec739SDaniel Rosenberg 		ch = exfat->upcase_table[le16_to_cpu(name[i])];
549*508ec739SDaniel Rosenberg 		ch = cpu_to_le16(ch);
550*508ec739SDaniel Rosenberg 
551*508ec739SDaniel Rosenberg 		chksum = ((chksum << 15) | (chksum >> 1)) + (ch & 0xFF);
552*508ec739SDaniel Rosenberg 		chksum = ((chksum << 15) | (chksum >> 1)) + (ch >> 8);
553*508ec739SDaniel Rosenberg 	}
554*508ec739SDaniel Rosenberg 	return chksum;
555*508ec739SDaniel Rosenberg }
556*508ec739SDaniel Rosenberg 
unix_time_to_exfat_time(time_t unix_time,__u8 * tz,__le16 * date,__le16 * time,__u8 * time_ms)557*508ec739SDaniel Rosenberg static void unix_time_to_exfat_time(time_t unix_time, __u8 *tz, __le16 *date,
558*508ec739SDaniel Rosenberg 				    __le16 *time, __u8 *time_ms)
559*508ec739SDaniel Rosenberg {
560*508ec739SDaniel Rosenberg 	struct tm tm;
561*508ec739SDaniel Rosenberg 	__u16 t, d;
562*508ec739SDaniel Rosenberg 
563*508ec739SDaniel Rosenberg 	gmtime_r(&unix_time, &tm);
564*508ec739SDaniel Rosenberg 	d = ((tm.tm_year - 80) << 9) | ((tm.tm_mon + 1) << 5) | tm.tm_mday;
565*508ec739SDaniel Rosenberg 	t = (tm.tm_hour << 11) | (tm.tm_min << 5) | (tm.tm_sec >> 1);
566*508ec739SDaniel Rosenberg 
567*508ec739SDaniel Rosenberg 	*tz = 0x80;
568*508ec739SDaniel Rosenberg 	*date = cpu_to_le16(d);
569*508ec739SDaniel Rosenberg 	*time = cpu_to_le16(t);
570*508ec739SDaniel Rosenberg 	if (time_ms)
571*508ec739SDaniel Rosenberg 		*time_ms = (tm.tm_sec & 1) * 100;
572*508ec739SDaniel Rosenberg }
573*508ec739SDaniel Rosenberg 
exfat_build_file_dentry_set(struct exfat * exfat,const char * name,unsigned short attr,struct exfat_dentry ** dentry_set,int * dentry_count)574*508ec739SDaniel Rosenberg int exfat_build_file_dentry_set(struct exfat *exfat, const char *name,
575*508ec739SDaniel Rosenberg 				unsigned short attr, struct exfat_dentry **dentry_set,
576*508ec739SDaniel Rosenberg 				int *dentry_count)
577*508ec739SDaniel Rosenberg {
578*508ec739SDaniel Rosenberg 	struct exfat_dentry *dset;
579*508ec739SDaniel Rosenberg 	__le16 utf16_name[PATH_MAX + 2];
580*508ec739SDaniel Rosenberg 	int retval;
581*508ec739SDaniel Rosenberg 	int dcount, name_len, i;
582*508ec739SDaniel Rosenberg 	__le16 e_date, e_time;
583*508ec739SDaniel Rosenberg 	__u8 tz, e_time_ms;
584*508ec739SDaniel Rosenberg 
585*508ec739SDaniel Rosenberg 	memset(utf16_name, 0, sizeof(utf16_name));
586*508ec739SDaniel Rosenberg 	retval = exfat_utf16_enc(name, utf16_name, sizeof(utf16_name));
587*508ec739SDaniel Rosenberg 	if (retval < 0)
588*508ec739SDaniel Rosenberg 		return retval;
589*508ec739SDaniel Rosenberg 
590*508ec739SDaniel Rosenberg 	name_len = retval / 2;
591*508ec739SDaniel Rosenberg 	dcount = 2 + DIV_ROUND_UP(name_len, ENTRY_NAME_MAX);
592*508ec739SDaniel Rosenberg 	dset = calloc(1, dcount * DENTRY_SIZE);
593*508ec739SDaniel Rosenberg 	if (!dset)
594*508ec739SDaniel Rosenberg 		return -ENOMEM;
595*508ec739SDaniel Rosenberg 
596*508ec739SDaniel Rosenberg 	dset[0].type = EXFAT_FILE;
597*508ec739SDaniel Rosenberg 	dset[0].dentry.file.num_ext = dcount - 1;
598*508ec739SDaniel Rosenberg 	dset[0].dentry.file.attr = cpu_to_le16(attr);
599*508ec739SDaniel Rosenberg 
600*508ec739SDaniel Rosenberg 	unix_time_to_exfat_time(time(NULL), &tz,
601*508ec739SDaniel Rosenberg 				&e_date, &e_time, &e_time_ms);
602*508ec739SDaniel Rosenberg 
603*508ec739SDaniel Rosenberg 	dset[0].dentry.file.create_date = e_date;
604*508ec739SDaniel Rosenberg 	dset[0].dentry.file.create_time = e_time;
605*508ec739SDaniel Rosenberg 	dset[0].dentry.file.create_time_ms = e_time_ms;
606*508ec739SDaniel Rosenberg 	dset[0].dentry.file.create_tz = tz;
607*508ec739SDaniel Rosenberg 
608*508ec739SDaniel Rosenberg 	dset[0].dentry.file.modify_date = e_date;
609*508ec739SDaniel Rosenberg 	dset[0].dentry.file.modify_time = e_time;
610*508ec739SDaniel Rosenberg 	dset[0].dentry.file.modify_time_ms = e_time_ms;
611*508ec739SDaniel Rosenberg 	dset[0].dentry.file.modify_tz = tz;
612*508ec739SDaniel Rosenberg 
613*508ec739SDaniel Rosenberg 	dset[0].dentry.file.access_date = e_date;
614*508ec739SDaniel Rosenberg 	dset[0].dentry.file.access_time = e_time;
615*508ec739SDaniel Rosenberg 	dset[0].dentry.file.access_tz = tz;
616*508ec739SDaniel Rosenberg 
617*508ec739SDaniel Rosenberg 	dset[1].type = EXFAT_STREAM;
618*508ec739SDaniel Rosenberg 	dset[1].dentry.stream.flags = 0x01;
619*508ec739SDaniel Rosenberg 	dset[1].dentry.stream.name_len = (__u8)name_len;
620*508ec739SDaniel Rosenberg 	dset[1].dentry.stream.name_hash =
621*508ec739SDaniel Rosenberg 		cpu_to_le16(exfat_calc_name_hash(exfat, utf16_name, name_len));
622*508ec739SDaniel Rosenberg 
623*508ec739SDaniel Rosenberg 	for (i = 2; i < dcount; i++) {
624*508ec739SDaniel Rosenberg 		dset[i].type = EXFAT_NAME;
625*508ec739SDaniel Rosenberg 		memcpy(dset[i].dentry.name.unicode_0_14,
626*508ec739SDaniel Rosenberg 		       utf16_name + (i - 2) * ENTRY_NAME_MAX,
627*508ec739SDaniel Rosenberg 		       ENTRY_NAME_MAX * 2);
628*508ec739SDaniel Rosenberg 	}
629*508ec739SDaniel Rosenberg 
630*508ec739SDaniel Rosenberg 	dset[0].dentry.file.checksum =
631*508ec739SDaniel Rosenberg 		cpu_to_le16(calc_dentry_set_checksum(dset, dcount));
632*508ec739SDaniel Rosenberg 
633*508ec739SDaniel Rosenberg 	*dentry_set = dset;
634*508ec739SDaniel Rosenberg 	*dentry_count = dcount;
635*508ec739SDaniel Rosenberg 	return 0;
636*508ec739SDaniel Rosenberg }
637*508ec739SDaniel Rosenberg 
exfat_update_file_dentry_set(struct exfat * exfat,struct exfat_dentry * dset,int dcount,const char * name,clus_t start_clu,clus_t ccount)638*508ec739SDaniel Rosenberg int exfat_update_file_dentry_set(struct exfat *exfat,
639*508ec739SDaniel Rosenberg 				 struct exfat_dentry *dset, int dcount,
640*508ec739SDaniel Rosenberg 				 const char *name,
641*508ec739SDaniel Rosenberg 				 clus_t start_clu, clus_t ccount)
642*508ec739SDaniel Rosenberg {
643*508ec739SDaniel Rosenberg 	int i, name_len;
644*508ec739SDaniel Rosenberg 	__le16 utf16_name[PATH_MAX + 2];
645*508ec739SDaniel Rosenberg 
646*508ec739SDaniel Rosenberg 	if (dset[0].type != EXFAT_FILE || dcount < MIN_FILE_DENTRIES)
647*508ec739SDaniel Rosenberg 		return -EINVAL;
648*508ec739SDaniel Rosenberg 
649*508ec739SDaniel Rosenberg 	if (name) {
650*508ec739SDaniel Rosenberg 		name_len = (int)exfat_utf16_enc(name,
651*508ec739SDaniel Rosenberg 						utf16_name, sizeof(utf16_name));
652*508ec739SDaniel Rosenberg 		if (name_len < 0)
653*508ec739SDaniel Rosenberg 			return name_len;
654*508ec739SDaniel Rosenberg 
655*508ec739SDaniel Rosenberg 		name_len /= 2;
656*508ec739SDaniel Rosenberg 		if (dcount != 2 + DIV_ROUND_UP(name_len, ENTRY_NAME_MAX))
657*508ec739SDaniel Rosenberg 			return -EINVAL;
658*508ec739SDaniel Rosenberg 
659*508ec739SDaniel Rosenberg 		dset[1].dentry.stream.name_len = (__u8)name_len;
660*508ec739SDaniel Rosenberg 		dset[1].dentry.stream.name_hash =
661*508ec739SDaniel Rosenberg 			exfat_calc_name_hash(exfat, utf16_name, name_len);
662*508ec739SDaniel Rosenberg 
663*508ec739SDaniel Rosenberg 		for (i = 2; i < dcount; i++) {
664*508ec739SDaniel Rosenberg 			dset[i].type = EXFAT_NAME;
665*508ec739SDaniel Rosenberg 			memcpy(dset[i].dentry.name.unicode_0_14,
666*508ec739SDaniel Rosenberg 			       utf16_name + (i - 2) * ENTRY_NAME_MAX,
667*508ec739SDaniel Rosenberg 			       ENTRY_NAME_MAX * 2);
668*508ec739SDaniel Rosenberg 		}
669*508ec739SDaniel Rosenberg 	}
670*508ec739SDaniel Rosenberg 
671*508ec739SDaniel Rosenberg 	dset[1].dentry.stream.valid_size = cpu_to_le64(ccount * exfat->clus_size);
672*508ec739SDaniel Rosenberg 	dset[1].dentry.stream.size = cpu_to_le64(ccount * exfat->clus_size);
673*508ec739SDaniel Rosenberg 	if (start_clu)
674*508ec739SDaniel Rosenberg 		dset[1].dentry.stream.start_clu = cpu_to_le32(start_clu);
675*508ec739SDaniel Rosenberg 
676*508ec739SDaniel Rosenberg 	dset[0].dentry.file.checksum =
677*508ec739SDaniel Rosenberg 		cpu_to_le16(calc_dentry_set_checksum(dset, dcount));
678*508ec739SDaniel Rosenberg 	return 0;
679*508ec739SDaniel Rosenberg }
680*508ec739SDaniel Rosenberg 
exfat_find_free_cluster(struct exfat * exfat,clus_t start,clus_t * new_clu)681*508ec739SDaniel Rosenberg int exfat_find_free_cluster(struct exfat *exfat,
682*508ec739SDaniel Rosenberg 			     clus_t start, clus_t *new_clu)
683*508ec739SDaniel Rosenberg {
684*508ec739SDaniel Rosenberg 	clus_t end = le32_to_cpu(exfat->bs->bsx.clu_count) +
685*508ec739SDaniel Rosenberg 		EXFAT_FIRST_CLUSTER;
686*508ec739SDaniel Rosenberg 
687*508ec739SDaniel Rosenberg 	if (!exfat_heap_clus(exfat, start))
688*508ec739SDaniel Rosenberg 		return -EINVAL;
689*508ec739SDaniel Rosenberg 
690*508ec739SDaniel Rosenberg 	while (start < end) {
691*508ec739SDaniel Rosenberg 		if (exfat_bitmap_find_zero(exfat, exfat->alloc_bitmap,
692*508ec739SDaniel Rosenberg 					   start, new_clu))
693*508ec739SDaniel Rosenberg 			break;
694*508ec739SDaniel Rosenberg 		if (!exfat_bitmap_get(exfat->disk_bitmap, *new_clu))
695*508ec739SDaniel Rosenberg 			return 0;
696*508ec739SDaniel Rosenberg 		start = *new_clu + 1;
697*508ec739SDaniel Rosenberg 	}
698*508ec739SDaniel Rosenberg 
699*508ec739SDaniel Rosenberg 	end = start;
700*508ec739SDaniel Rosenberg 	start = EXFAT_FIRST_CLUSTER;
701*508ec739SDaniel Rosenberg 	while (start < end) {
702*508ec739SDaniel Rosenberg 		if (exfat_bitmap_find_zero(exfat, exfat->alloc_bitmap,
703*508ec739SDaniel Rosenberg 					   start, new_clu))
704*508ec739SDaniel Rosenberg 			goto out_nospc;
705*508ec739SDaniel Rosenberg 		if (!exfat_bitmap_get(exfat->disk_bitmap, *new_clu))
706*508ec739SDaniel Rosenberg 			return 0;
707*508ec739SDaniel Rosenberg 		start = *new_clu + 1;
708*508ec739SDaniel Rosenberg 	}
709*508ec739SDaniel Rosenberg 
710*508ec739SDaniel Rosenberg out_nospc:
711*508ec739SDaniel Rosenberg 	*new_clu = EXFAT_EOF_CLUSTER;
712*508ec739SDaniel Rosenberg 	return -ENOSPC;
713*508ec739SDaniel Rosenberg }
714*508ec739SDaniel Rosenberg 
exfat_map_cluster(struct exfat * exfat,struct exfat_inode * inode,off_t file_off,clus_t * mapped_clu)715*508ec739SDaniel Rosenberg static int exfat_map_cluster(struct exfat *exfat, struct exfat_inode *inode,
716*508ec739SDaniel Rosenberg 			     off_t file_off, clus_t *mapped_clu)
717*508ec739SDaniel Rosenberg {
718*508ec739SDaniel Rosenberg 	clus_t clu, next, count, last_count;
719*508ec739SDaniel Rosenberg 
720*508ec739SDaniel Rosenberg 	if (!exfat_heap_clus(exfat, inode->first_clus))
721*508ec739SDaniel Rosenberg 		return -EINVAL;
722*508ec739SDaniel Rosenberg 
723*508ec739SDaniel Rosenberg 	clu = inode->first_clus;
724*508ec739SDaniel Rosenberg 	next = EXFAT_EOF_CLUSTER;
725*508ec739SDaniel Rosenberg 	count = 1;
726*508ec739SDaniel Rosenberg 	if (file_off == EOF)
727*508ec739SDaniel Rosenberg 		last_count = DIV_ROUND_UP(inode->size, exfat->clus_size);
728*508ec739SDaniel Rosenberg 	else
729*508ec739SDaniel Rosenberg 		last_count = file_off / exfat->clus_size + 1;
730*508ec739SDaniel Rosenberg 
731*508ec739SDaniel Rosenberg 	while (true) {
732*508ec739SDaniel Rosenberg 		if (count * exfat->clus_size > inode->size)
733*508ec739SDaniel Rosenberg 			return -EINVAL;
734*508ec739SDaniel Rosenberg 
735*508ec739SDaniel Rosenberg 		if (count == last_count) {
736*508ec739SDaniel Rosenberg 			*mapped_clu = clu;
737*508ec739SDaniel Rosenberg 			return 0;
738*508ec739SDaniel Rosenberg 		}
739*508ec739SDaniel Rosenberg 
740*508ec739SDaniel Rosenberg 		if (exfat_get_inode_next_clus(exfat, inode, clu, &next))
741*508ec739SDaniel Rosenberg 			return -EINVAL;
742*508ec739SDaniel Rosenberg 
743*508ec739SDaniel Rosenberg 		if (!exfat_heap_clus(exfat, clu))
744*508ec739SDaniel Rosenberg 			return -EINVAL;
745*508ec739SDaniel Rosenberg 
746*508ec739SDaniel Rosenberg 		clu = next;
747*508ec739SDaniel Rosenberg 		count++;
748*508ec739SDaniel Rosenberg 	}
749*508ec739SDaniel Rosenberg 	return -EINVAL;
750*508ec739SDaniel Rosenberg }
751*508ec739SDaniel Rosenberg 
exfat_write_dentry_set(struct exfat * exfat,struct exfat_dentry * dset,int dcount,off_t dev_off,off_t * next_dev_off)752*508ec739SDaniel Rosenberg static int exfat_write_dentry_set(struct exfat *exfat,
753*508ec739SDaniel Rosenberg 				  struct exfat_dentry *dset, int dcount,
754*508ec739SDaniel Rosenberg 				  off_t dev_off, off_t *next_dev_off)
755*508ec739SDaniel Rosenberg {
756*508ec739SDaniel Rosenberg 	clus_t clus;
757*508ec739SDaniel Rosenberg 	unsigned int clus_off, dent_len, first_half_len, sec_half_len;
758*508ec739SDaniel Rosenberg 	off_t first_half_off, sec_half_off = 0;
759*508ec739SDaniel Rosenberg 
760*508ec739SDaniel Rosenberg 	if (exfat_o2c(exfat, dev_off, &clus, &clus_off))
761*508ec739SDaniel Rosenberg 		return -ERANGE;
762*508ec739SDaniel Rosenberg 
763*508ec739SDaniel Rosenberg 	dent_len = dcount * DENTRY_SIZE;
764*508ec739SDaniel Rosenberg 	first_half_len = MIN(dent_len, exfat->clus_size - clus_off);
765*508ec739SDaniel Rosenberg 	sec_half_len = dent_len - first_half_len;
766*508ec739SDaniel Rosenberg 
767*508ec739SDaniel Rosenberg 	first_half_off = dev_off;
768*508ec739SDaniel Rosenberg 	if (sec_half_len) {
769*508ec739SDaniel Rosenberg 		clus_t next_clus;
770*508ec739SDaniel Rosenberg 
771*508ec739SDaniel Rosenberg 		if (exfat_get_next_clus(exfat, clus, &next_clus))
772*508ec739SDaniel Rosenberg 			return -EIO;
773*508ec739SDaniel Rosenberg 		if (!exfat_heap_clus(exfat, next_clus))
774*508ec739SDaniel Rosenberg 			return -EINVAL;
775*508ec739SDaniel Rosenberg 		sec_half_off = exfat_c2o(exfat, next_clus);
776*508ec739SDaniel Rosenberg 	}
777*508ec739SDaniel Rosenberg 
778*508ec739SDaniel Rosenberg 	if (exfat_write(exfat->blk_dev->dev_fd, dset, first_half_len,
779*508ec739SDaniel Rosenberg 			first_half_off) != (ssize_t)first_half_len)
780*508ec739SDaniel Rosenberg 		return -EIO;
781*508ec739SDaniel Rosenberg 
782*508ec739SDaniel Rosenberg 	if (sec_half_len) {
783*508ec739SDaniel Rosenberg 		dset = (struct exfat_dentry *)((char *)dset + first_half_len);
784*508ec739SDaniel Rosenberg 		if (exfat_write(exfat->blk_dev->dev_fd, dset, sec_half_len,
785*508ec739SDaniel Rosenberg 				sec_half_off) != (ssize_t)sec_half_len)
786*508ec739SDaniel Rosenberg 			return -EIO;
787*508ec739SDaniel Rosenberg 	}
788*508ec739SDaniel Rosenberg 
789*508ec739SDaniel Rosenberg 	if (next_dev_off) {
790*508ec739SDaniel Rosenberg 		if (sec_half_len)
791*508ec739SDaniel Rosenberg 			*next_dev_off = sec_half_off + sec_half_len;
792*508ec739SDaniel Rosenberg 		else
793*508ec739SDaniel Rosenberg 			*next_dev_off = first_half_off + first_half_len;
794*508ec739SDaniel Rosenberg 	}
795*508ec739SDaniel Rosenberg 	return 0;
796*508ec739SDaniel Rosenberg }
797*508ec739SDaniel Rosenberg 
exfat_alloc_cluster(struct exfat * exfat,struct exfat_inode * inode,clus_t * new_clu)798*508ec739SDaniel Rosenberg static int exfat_alloc_cluster(struct exfat *exfat, struct exfat_inode *inode,
799*508ec739SDaniel Rosenberg 			       clus_t *new_clu)
800*508ec739SDaniel Rosenberg {
801*508ec739SDaniel Rosenberg 	clus_t last_clu;
802*508ec739SDaniel Rosenberg 	int err;
803*508ec739SDaniel Rosenberg 	bool need_dset = inode != exfat->root;
804*508ec739SDaniel Rosenberg 
805*508ec739SDaniel Rosenberg 	if ((need_dset && !inode->dentry_set) || inode->is_contiguous)
806*508ec739SDaniel Rosenberg 		return -EINVAL;
807*508ec739SDaniel Rosenberg 
808*508ec739SDaniel Rosenberg 	err = exfat_find_free_cluster(exfat, exfat->start_clu, new_clu);
809*508ec739SDaniel Rosenberg 	if (err) {
810*508ec739SDaniel Rosenberg 		exfat->start_clu = EXFAT_FIRST_CLUSTER;
811*508ec739SDaniel Rosenberg 		exfat_err("failed to find an free cluster\n");
812*508ec739SDaniel Rosenberg 		return -ENOSPC;
813*508ec739SDaniel Rosenberg 	}
814*508ec739SDaniel Rosenberg 	exfat->start_clu = *new_clu;
815*508ec739SDaniel Rosenberg 
816*508ec739SDaniel Rosenberg 	if (exfat_set_fat(exfat, *new_clu, EXFAT_EOF_CLUSTER))
817*508ec739SDaniel Rosenberg 		return -EIO;
818*508ec739SDaniel Rosenberg 
819*508ec739SDaniel Rosenberg 	/* zero out the new cluster */
820*508ec739SDaniel Rosenberg 	if (exfat_write(exfat->blk_dev->dev_fd, exfat->zero_cluster,
821*508ec739SDaniel Rosenberg 			exfat->clus_size, exfat_c2o(exfat, *new_clu)) !=
822*508ec739SDaniel Rosenberg 	    (ssize_t)exfat->clus_size) {
823*508ec739SDaniel Rosenberg 		exfat_err("failed to fill new cluster with zeroes\n");
824*508ec739SDaniel Rosenberg 		return -EIO;
825*508ec739SDaniel Rosenberg 	}
826*508ec739SDaniel Rosenberg 
827*508ec739SDaniel Rosenberg 	if (inode->size) {
828*508ec739SDaniel Rosenberg 		err = exfat_map_cluster(exfat, inode, EOF, &last_clu);
829*508ec739SDaniel Rosenberg 		if (err) {
830*508ec739SDaniel Rosenberg 			exfat_err("failed to get the last cluster\n");
831*508ec739SDaniel Rosenberg 			return err;
832*508ec739SDaniel Rosenberg 		}
833*508ec739SDaniel Rosenberg 
834*508ec739SDaniel Rosenberg 		if (exfat_set_fat(exfat, last_clu, *new_clu))
835*508ec739SDaniel Rosenberg 			return -EIO;
836*508ec739SDaniel Rosenberg 
837*508ec739SDaniel Rosenberg 		if (need_dset) {
838*508ec739SDaniel Rosenberg 			err = exfat_update_file_dentry_set(exfat,
839*508ec739SDaniel Rosenberg 							   inode->dentry_set,
840*508ec739SDaniel Rosenberg 							   inode->dentry_count,
841*508ec739SDaniel Rosenberg 							   NULL, 0,
842*508ec739SDaniel Rosenberg 							   DIV_ROUND_UP(inode->size,
843*508ec739SDaniel Rosenberg 									exfat->clus_size) + 1);
844*508ec739SDaniel Rosenberg 			if (err)
845*508ec739SDaniel Rosenberg 				return -EINVAL;
846*508ec739SDaniel Rosenberg 		}
847*508ec739SDaniel Rosenberg 	} else {
848*508ec739SDaniel Rosenberg 		if (need_dset) {
849*508ec739SDaniel Rosenberg 			err = exfat_update_file_dentry_set(exfat,
850*508ec739SDaniel Rosenberg 							   inode->dentry_set,
851*508ec739SDaniel Rosenberg 							   inode->dentry_count,
852*508ec739SDaniel Rosenberg 							   NULL, *new_clu, 1);
853*508ec739SDaniel Rosenberg 			if (err)
854*508ec739SDaniel Rosenberg 				return -EINVAL;
855*508ec739SDaniel Rosenberg 		}
856*508ec739SDaniel Rosenberg 	}
857*508ec739SDaniel Rosenberg 
858*508ec739SDaniel Rosenberg 	if (need_dset && exfat_write_dentry_set(exfat, inode->dentry_set,
859*508ec739SDaniel Rosenberg 						inode->dentry_count,
860*508ec739SDaniel Rosenberg 						inode->dev_offset, NULL))
861*508ec739SDaniel Rosenberg 		return -EIO;
862*508ec739SDaniel Rosenberg 
863*508ec739SDaniel Rosenberg 	exfat_bitmap_set(exfat->alloc_bitmap, *new_clu);
864*508ec739SDaniel Rosenberg 	if (inode->size == 0)
865*508ec739SDaniel Rosenberg 		inode->first_clus = *new_clu;
866*508ec739SDaniel Rosenberg 	inode->size += exfat->clus_size;
867*508ec739SDaniel Rosenberg 	return 0;
868*508ec739SDaniel Rosenberg }
869*508ec739SDaniel Rosenberg 
exfat_add_dentry_set(struct exfat * exfat,struct exfat_dentry_loc * loc,struct exfat_dentry * dset,int dcount,bool need_next_loc)870*508ec739SDaniel Rosenberg int exfat_add_dentry_set(struct exfat *exfat, struct exfat_dentry_loc *loc,
871*508ec739SDaniel Rosenberg 			 struct exfat_dentry *dset, int dcount,
872*508ec739SDaniel Rosenberg 			 bool need_next_loc)
873*508ec739SDaniel Rosenberg {
874*508ec739SDaniel Rosenberg 	struct exfat_inode *parent = loc->parent;
875*508ec739SDaniel Rosenberg 	off_t dev_off, next_dev_off;
876*508ec739SDaniel Rosenberg 
877*508ec739SDaniel Rosenberg 	if (parent->is_contiguous ||
878*508ec739SDaniel Rosenberg 	    (uint64_t)loc->file_offset > parent->size ||
879*508ec739SDaniel Rosenberg 	    (unsigned int)dcount * DENTRY_SIZE > exfat->clus_size)
880*508ec739SDaniel Rosenberg 		return -EINVAL;
881*508ec739SDaniel Rosenberg 
882*508ec739SDaniel Rosenberg 	dev_off = loc->dev_offset;
883*508ec739SDaniel Rosenberg 	if ((uint64_t)loc->file_offset + dcount * DENTRY_SIZE > parent->size) {
884*508ec739SDaniel Rosenberg 		clus_t new_clus;
885*508ec739SDaniel Rosenberg 
886*508ec739SDaniel Rosenberg 		if (exfat_alloc_cluster(exfat, parent, &new_clus))
887*508ec739SDaniel Rosenberg 			return -EIO;
888*508ec739SDaniel Rosenberg 		if ((uint64_t)loc->file_offset == parent->size - exfat->clus_size)
889*508ec739SDaniel Rosenberg 			dev_off = exfat_c2o(exfat, new_clus);
890*508ec739SDaniel Rosenberg 	}
891*508ec739SDaniel Rosenberg 
892*508ec739SDaniel Rosenberg 	if (exfat_write_dentry_set(exfat, dset, dcount, dev_off, &next_dev_off))
893*508ec739SDaniel Rosenberg 		return -EIO;
894*508ec739SDaniel Rosenberg 
895*508ec739SDaniel Rosenberg 	if (need_next_loc) {
896*508ec739SDaniel Rosenberg 		loc->file_offset += dcount * DENTRY_SIZE;
897*508ec739SDaniel Rosenberg 		loc->dev_offset = next_dev_off;
898*508ec739SDaniel Rosenberg 	}
899*508ec739SDaniel Rosenberg 	return 0;
900*508ec739SDaniel Rosenberg }
901*508ec739SDaniel Rosenberg 
exfat_create_file(struct exfat * exfat,struct exfat_inode * parent,const char * name,unsigned short attr)902*508ec739SDaniel Rosenberg int exfat_create_file(struct exfat *exfat, struct exfat_inode *parent,
903*508ec739SDaniel Rosenberg 		      const char *name, unsigned short attr)
904*508ec739SDaniel Rosenberg {
905*508ec739SDaniel Rosenberg 	struct exfat_dentry *dset;
906*508ec739SDaniel Rosenberg 	int err, dcount;
907*508ec739SDaniel Rosenberg 	struct exfat_lookup_filter filter;
908*508ec739SDaniel Rosenberg 	struct exfat_dentry_loc loc;
909*508ec739SDaniel Rosenberg 
910*508ec739SDaniel Rosenberg 	err = exfat_lookup_file(exfat, parent, name, &filter);
911*508ec739SDaniel Rosenberg 	if (err == 0) {
912*508ec739SDaniel Rosenberg 		dset = filter.out.dentry_set;
913*508ec739SDaniel Rosenberg 		dcount = filter.out.dentry_count;
914*508ec739SDaniel Rosenberg 		if ((le16_to_cpu(dset->dentry.file.attr) & attr) != attr)
915*508ec739SDaniel Rosenberg 			err = -EEXIST;
916*508ec739SDaniel Rosenberg 		goto out;
917*508ec739SDaniel Rosenberg 	}
918*508ec739SDaniel Rosenberg 
919*508ec739SDaniel Rosenberg 	err = exfat_build_file_dentry_set(exfat, name, attr,
920*508ec739SDaniel Rosenberg 					  &dset, &dcount);
921*508ec739SDaniel Rosenberg 	if (err)
922*508ec739SDaniel Rosenberg 		return err;
923*508ec739SDaniel Rosenberg 
924*508ec739SDaniel Rosenberg 	loc.parent = parent;
925*508ec739SDaniel Rosenberg 	loc.file_offset = filter.out.file_offset;
926*508ec739SDaniel Rosenberg 	loc.dev_offset = filter.out.dev_offset;
927*508ec739SDaniel Rosenberg 	err = exfat_add_dentry_set(exfat, &loc, dset, dcount, false);
928*508ec739SDaniel Rosenberg out:
929*508ec739SDaniel Rosenberg 	free(dset);
930*508ec739SDaniel Rosenberg 	return err;
931*508ec739SDaniel Rosenberg }
932