blob: 59cffef34e66e2d67720f6a5838ed3b8434f3b43 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Donggeun Kim8f814002011-10-24 21:15:28 +00002/*
3 * fat_write.c
4 *
5 * R/W (V)FAT 12/16/32 filesystem implementation by Donggeun Kim
Donggeun Kim8f814002011-10-24 21:15:28 +00006 */
7
8#include <common.h>
9#include <command.h>
10#include <config.h>
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +010011#include <div64.h>
Donggeun Kim8f814002011-10-24 21:15:28 +000012#include <fat.h>
Simon Glass0f2af882020-05-10 11:40:05 -060013#include <log.h>
Simon Glass9bc15642020-02-03 07:36:16 -070014#include <malloc.h>
Donggeun Kim8f814002011-10-24 21:15:28 +000015#include <part.h>
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +010016#include <rand.h>
17#include <asm/byteorder.h>
Simon Glass274e0b02020-05-10 11:39:56 -060018#include <asm/cache.h>
Richard Genoud2e372022012-12-13 00:47:36 +000019#include <linux/ctype.h>
Tom Rinia17b7bc2014-11-24 11:50:46 -050020#include <linux/math64.h>
Donggeun Kim8f814002011-10-24 21:15:28 +000021#include "fat.c"
22
Heinrich Schuchardt24977e52020-11-25 16:33:55 +010023static dir_entry *find_directory_entry(fat_itr *itr, char *filename);
24
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +010025/* Characters that may only be used in long file names */
26static const char LONG_ONLY_CHARS[] = "+,;=[]";
27
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +010028/* Combined size of the name and ext fields in the directory entry */
29#define SHORT_NAME_SIZE 11
30
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +010031/**
32 * str2fat() - convert string to valid FAT name characters
33 *
34 * Stop when reaching end of @src or a period.
35 * Ignore spaces.
36 * Replace characters that may only be used in long names by underscores.
37 * Convert lower case characters to upper case.
38 *
39 * To avoid assumptions about the code page we do not use characters
40 * above 0x7f for the short name.
41 *
42 * @dest: destination buffer
43 * @src: source buffer
44 * @length: size of destination buffer
45 * Return: number of bytes in destination buffer
46 */
47static int str2fat(char *dest, char *src, int length)
Donggeun Kim8f814002011-10-24 21:15:28 +000048{
49 int i;
50
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +010051 for (i = 0; i < length; ++src) {
52 char c = *src;
53
54 if (!c || c == '.')
55 break;
56 if (c == ' ')
57 continue;
58 if (strchr(LONG_ONLY_CHARS, c) || c > 0x7f)
59 c = '_';
60 else if (c >= 'a' && c <= 'z')
61 c &= 0xdf;
62 dest[i] = c;
63 ++i;
Donggeun Kim8f814002011-10-24 21:15:28 +000064 }
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +010065 return i;
Donggeun Kim8f814002011-10-24 21:15:28 +000066}
67
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +010068/**
Heinrich Schuchardt24977e52020-11-25 16:33:55 +010069 * fat_move_to_cluster() - position to first directory entry in cluster
70 *
71 * @itr: directory iterator
72 * @cluster cluster
73 * Return: 0 for success, -EIO on error
74 */
75static int fat_move_to_cluster(fat_itr *itr, unsigned int cluster)
76{
77 unsigned int nbytes;
78
79 /* position to the start of the directory */
80 itr->next_clust = cluster;
81 itr->last_cluster = 0;
82 if (!fat_next_cluster(itr, &nbytes))
83 return -EIO;
84 itr->dent = (dir_entry *)itr->block;
85 itr->remaining = nbytes / sizeof(dir_entry) - 1;
86 return 0;
87}
88
89/**
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +010090 * set_name() - set short name in directory entry
91 *
92 * The function determines if the @filename is a valid short name.
93 * In this case no long name is needed.
94 *
95 * If a long name is needed, a short name is constructed.
96 *
Heinrich Schuchardt24977e52020-11-25 16:33:55 +010097 * @itr: directory iterator
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +010098 * @filename: long file name
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +010099 * @shortname: buffer of 11 bytes to receive chosen short name and extension
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +0100100 * Return: number of directory entries needed, negative on error
101 */
Heinrich Schuchardt24977e52020-11-25 16:33:55 +0100102static int set_name(fat_itr *itr, const char *filename, char *shortname)
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +0100103{
104 char *period;
105 char *pos;
106 int period_location;
107 char buf[13];
108 int i;
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +0100109 int ret;
110 struct {
111 char name[8];
112 char ext[3];
113 } dirent;
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +0100114
115 if (!filename)
116 return -EIO;
117
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +0100118 /* Initialize buffer */
119 memset(&dirent, ' ', sizeof(dirent));
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +0100120
121 /* Convert filename to upper case short name */
122 period = strrchr(filename, '.');
123 pos = (char *)filename;
124 if (*pos == '.') {
125 pos = period + 1;
126 period = 0;
127 }
128 if (period)
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +0100129 str2fat(dirent.ext, period + 1, sizeof(dirent.ext));
130 period_location = str2fat(dirent.name, pos, sizeof(dirent.name));
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +0100131 if (period_location < 0)
132 return period_location;
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +0100133 if (*dirent.name == ' ')
134 *dirent.name = '_';
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +0100135 /* 0xe5 signals a deleted directory entry. Replace it by 0x05. */
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +0100136 if (*dirent.name == 0xe5)
137 *dirent.name = 0x05;
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +0100138
139 /* If filename and short name are the same, quit. */
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +0100140 sprintf(buf, "%.*s.%.3s", period_location, dirent.name, dirent.ext);
141 if (!strcmp(buf, filename)) {
142 ret = 1;
143 goto out;
144 }
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +0100145
146 /* Construct an indexed short name */
147 for (i = 1; i < 0x200000; ++i) {
148 int suffix_len;
149 int suffix_start;
150 int j;
151
152 /* To speed up the search use random numbers */
153 if (i < 10) {
154 j = i;
155 } else {
156 j = 30 - fls(i);
157 j = 10 + (rand() >> j);
158 }
159 sprintf(buf, "~%d", j);
160 suffix_len = strlen(buf);
161 suffix_start = 8 - suffix_len;
162 if (suffix_start > period_location)
163 suffix_start = period_location;
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +0100164 memcpy(dirent.name + suffix_start, buf, suffix_len);
165 if (*dirent.ext != ' ')
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +0100166 sprintf(buf, "%.*s.%.3s", suffix_start + suffix_len,
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +0100167 dirent.name, dirent.ext);
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +0100168 else
169 sprintf(buf, "%.*s", suffix_start + suffix_len,
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +0100170 dirent.name);
Heinrich Schuchardt24977e52020-11-25 16:33:55 +0100171 debug("generated short name: %s\n", buf);
172
173 /* Check that the short name does not exist yet. */
174 ret = fat_move_to_cluster(itr, itr->start_clust);
175 if (ret)
176 return ret;
177 if (find_directory_entry(itr, buf))
178 continue;
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +0100179
Heinrich Schuchardt24977e52020-11-25 16:33:55 +0100180 debug("chosen short name: %s\n", buf);
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +0100181 /* Each long name directory entry takes 13 characters. */
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +0100182 ret = (strlen(filename) + 25) / 13;
183 goto out;
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +0100184 }
185 return -EIO;
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +0100186out:
187 memcpy(shortname, dirent.name, SHORT_NAME_SIZE);
188 return ret;
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +0100189}
190
Donggeun Kim8f814002011-10-24 21:15:28 +0000191static int total_sector;
Donggeun Kimcb5f3bc2012-03-22 04:38:55 +0000192static int disk_write(__u32 block, __u32 nr_blocks, void *buf)
Donggeun Kim8f814002011-10-24 21:15:28 +0000193{
Łukasz Majewskid6f23d82015-09-03 14:21:39 +0200194 ulong ret;
195
Simon Glass2ee8ada2016-02-29 15:25:52 -0700196 if (!cur_dev)
Donggeun Kim8f814002011-10-24 21:15:28 +0000197 return -1;
198
Donggeun Kimcb5f3bc2012-03-22 04:38:55 +0000199 if (cur_part_info.start + block + nr_blocks >
200 cur_part_info.start + total_sector) {
Donggeun Kim8f814002011-10-24 21:15:28 +0000201 printf("error: overflow occurs\n");
202 return -1;
203 }
204
Simon Glass2ee8ada2016-02-29 15:25:52 -0700205 ret = blk_dwrite(cur_dev, cur_part_info.start + block, nr_blocks, buf);
Łukasz Majewskid6f23d82015-09-03 14:21:39 +0200206 if (nr_blocks && ret == 0)
207 return -1;
208
209 return ret;
Donggeun Kim8f814002011-10-24 21:15:28 +0000210}
211
Donggeun Kim8f814002011-10-24 21:15:28 +0000212/*
213 * Write fat buffer into block device
214 */
Stefan Brüns751b31d2016-09-11 22:51:40 +0200215static int flush_dirty_fat_buffer(fsdata *mydata)
Donggeun Kim8f814002011-10-24 21:15:28 +0000216{
217 int getsize = FATBUFBLOCKS;
218 __u32 fatlength = mydata->fatlength;
219 __u8 *bufptr = mydata->fatbuf;
220 __u32 startblock = mydata->fatbufnum * FATBUFBLOCKS;
221
Stefan Brüns751b31d2016-09-11 22:51:40 +0200222 debug("debug: evicting %d, dirty: %d\n", mydata->fatbufnum,
223 (int)mydata->fat_dirty);
224
225 if ((!mydata->fat_dirty) || (mydata->fatbufnum == -1))
226 return 0;
227
Stefan Brüns58bf86f2016-12-17 00:27:50 +0100228 /* Cap length if fatlength is not a multiple of FATBUFBLOCKS */
229 if (startblock + getsize > fatlength)
230 getsize = fatlength - startblock;
Donggeun Kim8f814002011-10-24 21:15:28 +0000231
Stefan Brüns58bf86f2016-12-17 00:27:50 +0100232 startblock += mydata->fat_sect;
Donggeun Kim8f814002011-10-24 21:15:28 +0000233
234 /* Write FAT buf */
235 if (disk_write(startblock, getsize, bufptr) < 0) {
236 debug("error: writing FAT blocks\n");
237 return -1;
238 }
239
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +0900240 if (mydata->fats == 2) {
Donggeun Kimaf57ce22011-12-20 18:34:27 +0000241 /* Update corresponding second FAT blocks */
242 startblock += mydata->fatlength;
243 if (disk_write(startblock, getsize, bufptr) < 0) {
244 debug("error: writing second FAT blocks\n");
245 return -1;
246 }
247 }
Stefan Brüns751b31d2016-09-11 22:51:40 +0200248 mydata->fat_dirty = 0;
Donggeun Kimaf57ce22011-12-20 18:34:27 +0000249
Donggeun Kim8f814002011-10-24 21:15:28 +0000250 return 0;
251}
252
253/*
Donggeun Kim8f814002011-10-24 21:15:28 +0000254 * Set the file name information from 'name' into 'slotptr',
255 */
256static int str2slot(dir_slot *slotptr, const char *name, int *idx)
257{
258 int j, end_idx = 0;
259
260 for (j = 0; j <= 8; j += 2) {
261 if (name[*idx] == 0x00) {
262 slotptr->name0_4[j] = 0;
263 slotptr->name0_4[j + 1] = 0;
264 end_idx++;
265 goto name0_4;
266 }
267 slotptr->name0_4[j] = name[*idx];
268 (*idx)++;
269 end_idx++;
270 }
271 for (j = 0; j <= 10; j += 2) {
272 if (name[*idx] == 0x00) {
273 slotptr->name5_10[j] = 0;
274 slotptr->name5_10[j + 1] = 0;
275 end_idx++;
276 goto name5_10;
277 }
278 slotptr->name5_10[j] = name[*idx];
279 (*idx)++;
280 end_idx++;
281 }
282 for (j = 0; j <= 2; j += 2) {
283 if (name[*idx] == 0x00) {
284 slotptr->name11_12[j] = 0;
285 slotptr->name11_12[j + 1] = 0;
286 end_idx++;
287 goto name11_12;
288 }
289 slotptr->name11_12[j] = name[*idx];
290 (*idx)++;
291 end_idx++;
292 }
293
294 if (name[*idx] == 0x00)
295 return 1;
296
297 return 0;
298/* Not used characters are filled with 0xff 0xff */
299name0_4:
300 for (; end_idx < 5; end_idx++) {
301 slotptr->name0_4[end_idx * 2] = 0xff;
302 slotptr->name0_4[end_idx * 2 + 1] = 0xff;
303 }
304 end_idx = 5;
305name5_10:
306 end_idx -= 5;
307 for (; end_idx < 6; end_idx++) {
308 slotptr->name5_10[end_idx * 2] = 0xff;
309 slotptr->name5_10[end_idx * 2 + 1] = 0xff;
310 }
311 end_idx = 11;
312name11_12:
313 end_idx -= 11;
314 for (; end_idx < 2; end_idx++) {
315 slotptr->name11_12[end_idx * 2] = 0xff;
316 slotptr->name11_12[end_idx * 2 + 1] = 0xff;
317 }
318
319 return 1;
320}
321
AKASHI Takahiroa2c0bd02019-05-24 14:10:36 +0900322static int new_dir_table(fat_itr *itr);
323static int flush_dir(fat_itr *itr);
Donggeun Kim8f814002011-10-24 21:15:28 +0000324
Heinrich Schuchardtac4ab752020-11-21 08:32:50 +0100325/**
326 * fill_dir_slot() - fill directory entries for long name
327 *
328 * @itr: directory iterator
329 * @l_name: long name
330 * @shortname: short name
331 * Return: 0 for success, -errno otherwise
Donggeun Kim8f814002011-10-24 21:15:28 +0000332 */
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +0900333static int
Heinrich Schuchardtac4ab752020-11-21 08:32:50 +0100334fill_dir_slot(fat_itr *itr, const char *l_name, const char *shortname)
Donggeun Kim8f814002011-10-24 21:15:28 +0000335{
Tien Fong Chee0117dbe2016-07-27 23:08:56 -0700336 __u8 temp_dir_slot_buffer[MAX_LFN_SLOT * sizeof(dir_slot)];
337 dir_slot *slotptr = (dir_slot *)temp_dir_slot_buffer;
Anatolij Gustschinf4f9d5d2011-12-15 03:12:14 +0000338 __u8 counter = 0, checksum;
Donggeun Kim8f814002011-10-24 21:15:28 +0000339 int idx = 0, ret;
Donggeun Kim8f814002011-10-24 21:15:28 +0000340
Stefan Brüns6c617d62016-09-11 22:51:39 +0200341 /* Get short file name checksum value */
Heinrich Schuchardtac4ab752020-11-21 08:32:50 +0100342 checksum = mkcksum(shortname, shortname + 8);
Donggeun Kim8f814002011-10-24 21:15:28 +0000343
344 do {
345 memset(slotptr, 0x00, sizeof(dir_slot));
346 ret = str2slot(slotptr, l_name, &idx);
347 slotptr->id = ++counter;
348 slotptr->attr = ATTR_VFAT;
349 slotptr->alias_checksum = checksum;
350 slotptr++;
351 } while (ret == 0);
352
353 slotptr--;
354 slotptr->id |= LAST_LONG_ENTRY_MASK;
355
356 while (counter >= 1) {
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +0900357 memcpy(itr->dent, slotptr, sizeof(dir_slot));
Donggeun Kim8f814002011-10-24 21:15:28 +0000358 slotptr--;
359 counter--;
AKASHI Takahiroa2c0bd02019-05-24 14:10:36 +0900360
361 if (itr->remaining == 0)
362 flush_dir(itr);
363
AKASHI Takahiroc3269332019-05-24 14:10:37 +0900364 /* allocate a cluster for more entries */
Heinrich Schuchardta8021522020-11-19 12:24:44 +0100365 if (!fat_itr_next(itr) && !itr->dent)
366 if ((itr->is_root && itr->fsdata->fatsize != 32) ||
AKASHI Takahiroc3269332019-05-24 14:10:37 +0900367 new_dir_table(itr))
Heinrich Schuchardtac4ab752020-11-21 08:32:50 +0100368 return -EIO;
Donggeun Kim8f814002011-10-24 21:15:28 +0000369 }
370
Donggeun Kim8f814002011-10-24 21:15:28 +0000371 return 0;
372}
373
Donggeun Kim8f814002011-10-24 21:15:28 +0000374/*
Philipp Skadorov16f553d2016-12-15 15:52:53 -0500375 * Set the entry at index 'entry' in a FAT (12/16/32) table.
Donggeun Kim8f814002011-10-24 21:15:28 +0000376 */
377static int set_fatent_value(fsdata *mydata, __u32 entry, __u32 entry_value)
378{
Philipp Skadorov16f553d2016-12-15 15:52:53 -0500379 __u32 bufnum, offset, off16;
380 __u16 val1, val2;
Donggeun Kim8f814002011-10-24 21:15:28 +0000381
382 switch (mydata->fatsize) {
383 case 32:
384 bufnum = entry / FAT32BUFSIZE;
385 offset = entry - bufnum * FAT32BUFSIZE;
386 break;
387 case 16:
388 bufnum = entry / FAT16BUFSIZE;
389 offset = entry - bufnum * FAT16BUFSIZE;
390 break;
Philipp Skadorov16f553d2016-12-15 15:52:53 -0500391 case 12:
392 bufnum = entry / FAT12BUFSIZE;
393 offset = entry - bufnum * FAT12BUFSIZE;
394 break;
Donggeun Kim8f814002011-10-24 21:15:28 +0000395 default:
396 /* Unsupported FAT size */
397 return -1;
398 }
399
400 /* Read a new block of FAT entries into the cache. */
401 if (bufnum != mydata->fatbufnum) {
402 int getsize = FATBUFBLOCKS;
403 __u8 *bufptr = mydata->fatbuf;
404 __u32 fatlength = mydata->fatlength;
405 __u32 startblock = bufnum * FATBUFBLOCKS;
406
Stefan Brüns58bf86f2016-12-17 00:27:50 +0100407 /* Cap length if fatlength is not a multiple of FATBUFBLOCKS */
408 if (startblock + getsize > fatlength)
409 getsize = fatlength - startblock;
Donggeun Kim8f814002011-10-24 21:15:28 +0000410
Stefan Brüns751b31d2016-09-11 22:51:40 +0200411 if (flush_dirty_fat_buffer(mydata) < 0)
412 return -1;
Donggeun Kim8f814002011-10-24 21:15:28 +0000413
Stefan Brüns58bf86f2016-12-17 00:27:50 +0100414 startblock += mydata->fat_sect;
415
Donggeun Kim8f814002011-10-24 21:15:28 +0000416 if (disk_read(startblock, getsize, bufptr) < 0) {
417 debug("Error reading FAT blocks\n");
418 return -1;
419 }
420 mydata->fatbufnum = bufnum;
421 }
422
Stefan Brüns751b31d2016-09-11 22:51:40 +0200423 /* Mark as dirty */
424 mydata->fat_dirty = 1;
425
Donggeun Kim8f814002011-10-24 21:15:28 +0000426 /* Set the actual entry */
427 switch (mydata->fatsize) {
428 case 32:
429 ((__u32 *) mydata->fatbuf)[offset] = cpu_to_le32(entry_value);
430 break;
431 case 16:
432 ((__u16 *) mydata->fatbuf)[offset] = cpu_to_le16(entry_value);
433 break;
Philipp Skadorov16f553d2016-12-15 15:52:53 -0500434 case 12:
435 off16 = (offset * 3) / 4;
436
437 switch (offset & 0x3) {
438 case 0:
439 val1 = cpu_to_le16(entry_value) & 0xfff;
440 ((__u16 *)mydata->fatbuf)[off16] &= ~0xfff;
441 ((__u16 *)mydata->fatbuf)[off16] |= val1;
442 break;
443 case 1:
444 val1 = cpu_to_le16(entry_value) & 0xf;
445 val2 = (cpu_to_le16(entry_value) >> 4) & 0xff;
446
447 ((__u16 *)mydata->fatbuf)[off16] &= ~0xf000;
448 ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 12);
449
450 ((__u16 *)mydata->fatbuf)[off16 + 1] &= ~0xff;
451 ((__u16 *)mydata->fatbuf)[off16 + 1] |= val2;
452 break;
453 case 2:
454 val1 = cpu_to_le16(entry_value) & 0xff;
455 val2 = (cpu_to_le16(entry_value) >> 8) & 0xf;
456
457 ((__u16 *)mydata->fatbuf)[off16] &= ~0xff00;
458 ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 8);
459
460 ((__u16 *)mydata->fatbuf)[off16 + 1] &= ~0xf;
461 ((__u16 *)mydata->fatbuf)[off16 + 1] |= val2;
462 break;
463 case 3:
464 val1 = cpu_to_le16(entry_value) & 0xfff;
465 ((__u16 *)mydata->fatbuf)[off16] &= ~0xfff0;
466 ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 4);
467 break;
468 default:
469 break;
470 }
471
472 break;
Donggeun Kim8f814002011-10-24 21:15:28 +0000473 default:
474 return -1;
475 }
476
477 return 0;
478}
479
480/*
Philipp Skadorov16f553d2016-12-15 15:52:53 -0500481 * Determine the next free cluster after 'entry' in a FAT (12/16/32) table
Stefan Brüns14999402016-09-11 22:51:41 +0200482 * and link it to 'entry'. EOC marker is not set on returned entry.
Donggeun Kim8f814002011-10-24 21:15:28 +0000483 */
484static __u32 determine_fatent(fsdata *mydata, __u32 entry)
485{
486 __u32 next_fat, next_entry = entry + 1;
487
488 while (1) {
Stefan Brüns9f95d812016-12-17 00:27:51 +0100489 next_fat = get_fatent(mydata, next_entry);
Donggeun Kim8f814002011-10-24 21:15:28 +0000490 if (next_fat == 0) {
Stefan Brüns14999402016-09-11 22:51:41 +0200491 /* found free entry, link to entry */
Donggeun Kim8f814002011-10-24 21:15:28 +0000492 set_fatent_value(mydata, entry, next_entry);
493 break;
494 }
495 next_entry++;
496 }
497 debug("FAT%d: entry: %08x, entry_value: %04x\n",
498 mydata->fatsize, entry, next_entry);
499
500 return next_entry;
501}
502
Heinrich Schuchardt9fb8db42018-10-02 09:30:45 +0200503/**
AKASHI Takahirod98c6742019-05-24 14:10:35 +0900504 * set_sectors() - write data to sectors
Heinrich Schuchardt9fb8db42018-10-02 09:30:45 +0200505 *
AKASHI Takahirod98c6742019-05-24 14:10:35 +0900506 * Write 'size' bytes from 'buffer' into the specified sector.
Heinrich Schuchardt9fb8db42018-10-02 09:30:45 +0200507 *
508 * @mydata: data to be written
AKASHI Takahirod98c6742019-05-24 14:10:35 +0900509 * @startsect: sector to be written to
Heinrich Schuchardt9fb8db42018-10-02 09:30:45 +0200510 * @buffer: data to be written
511 * @size: bytes to be written (but not more than the size of a cluster)
512 * Return: 0 on success, -1 otherwise
Donggeun Kim8f814002011-10-24 21:15:28 +0000513 */
514static int
AKASHI Takahirod98c6742019-05-24 14:10:35 +0900515set_sectors(fsdata *mydata, u32 startsect, u8 *buffer, u32 size)
Donggeun Kim8f814002011-10-24 21:15:28 +0000516{
AKASHI Takahirod98c6742019-05-24 14:10:35 +0900517 u32 nsects = 0;
Benoît Thébaudeauccc945b2015-09-28 15:45:28 +0200518 int ret;
Donggeun Kim8f814002011-10-24 21:15:28 +0000519
AKASHI Takahirod98c6742019-05-24 14:10:35 +0900520 debug("startsect: %d\n", startsect);
Donggeun Kim8f814002011-10-24 21:15:28 +0000521
Benoît Thébaudeauccc945b2015-09-28 15:45:28 +0200522 if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {
523 ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
524
Heinrich Schuchardtaa9d6ba2018-09-13 19:42:47 +0200525 debug("FAT: Misaligned buffer address (%p)\n", buffer);
Benoît Thébaudeauccc945b2015-09-28 15:45:28 +0200526
527 while (size >= mydata->sect_size) {
528 memcpy(tmpbuf, buffer, mydata->sect_size);
529 ret = disk_write(startsect++, 1, tmpbuf);
530 if (ret != 1) {
531 debug("Error writing data (got %d)\n", ret);
532 return -1;
533 }
534
535 buffer += mydata->sect_size;
536 size -= mydata->sect_size;
537 }
538 } else if (size >= mydata->sect_size) {
AKASHI Takahirod98c6742019-05-24 14:10:35 +0900539 nsects = size / mydata->sect_size;
540 ret = disk_write(startsect, nsects, buffer);
541 if (ret != nsects) {
Benoît Thébaudeauccc945b2015-09-28 15:45:28 +0200542 debug("Error writing data (got %d)\n", ret);
Wu, Joshb0e38dd2013-07-24 17:55:30 +0800543 return -1;
544 }
Donggeun Kim8f814002011-10-24 21:15:28 +0000545
AKASHI Takahirod98c6742019-05-24 14:10:35 +0900546 startsect += nsects;
547 buffer += nsects * mydata->sect_size;
548 size -= nsects * mydata->sect_size;
Benoît Thébaudeauccc945b2015-09-28 15:45:28 +0200549 }
Donggeun Kim8f814002011-10-24 21:15:28 +0000550
Benoît Thébaudeauccc945b2015-09-28 15:45:28 +0200551 if (size) {
552 ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
Heinrich Schuchardt9fb8db42018-10-02 09:30:45 +0200553 /* Do not leak content of stack */
554 memset(tmpbuf, 0, mydata->sect_size);
Benoît Thébaudeauccc945b2015-09-28 15:45:28 +0200555 memcpy(tmpbuf, buffer, size);
556 ret = disk_write(startsect, 1, tmpbuf);
557 if (ret != 1) {
558 debug("Error writing data (got %d)\n", ret);
Donggeun Kim8f814002011-10-24 21:15:28 +0000559 return -1;
560 }
Donggeun Kim8f814002011-10-24 21:15:28 +0000561 }
562
563 return 0;
564}
565
AKASHI Takahirod98c6742019-05-24 14:10:35 +0900566/**
567 * set_cluster() - write data to cluster
568 *
569 * Write 'size' bytes from 'buffer' into the specified cluster.
570 *
571 * @mydata: data to be written
572 * @clustnum: cluster to be written to
573 * @buffer: data to be written
574 * @size: bytes to be written (but not more than the size of a cluster)
575 * Return: 0 on success, -1 otherwise
576 */
577static int
578set_cluster(fsdata *mydata, u32 clustnum, u8 *buffer, u32 size)
579{
580 return set_sectors(mydata, clust_to_sect(mydata, clustnum),
581 buffer, size);
582}
583
584static int
585flush_dir(fat_itr *itr)
586{
587 fsdata *mydata = itr->fsdata;
588 u32 startsect, sect_offset, nsects;
589
590 if (!itr->is_root || mydata->fatsize == 32)
591 return set_cluster(mydata, itr->clust, itr->block,
592 mydata->clust_size * mydata->sect_size);
593
594 sect_offset = itr->clust * mydata->clust_size;
595 startsect = mydata->rootdir_sect + sect_offset;
596 /* do not write past the end of rootdir */
597 nsects = min_t(u32, mydata->clust_size,
598 mydata->rootdir_size - sect_offset);
599
600 return set_sectors(mydata, startsect, itr->block,
601 nsects * mydata->sect_size);
602}
603
AKASHI Takahirob07f7042018-09-11 15:59:06 +0900604/*
605 * Read and modify data on existing and consecutive cluster blocks
606 */
607static int
608get_set_cluster(fsdata *mydata, __u32 clustnum, loff_t pos, __u8 *buffer,
609 loff_t size, loff_t *gotsize)
610{
Heinrich Schuchardt8a3ac6e2020-07-06 07:48:14 +0200611 static u8 *tmpbuf_cluster;
AKASHI Takahirob07f7042018-09-11 15:59:06 +0900612 unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
613 __u32 startsect;
614 loff_t wsize;
615 int clustcount, i, ret;
616
617 *gotsize = 0;
618 if (!size)
619 return 0;
620
Heinrich Schuchardt8a3ac6e2020-07-06 07:48:14 +0200621 if (!tmpbuf_cluster) {
622 tmpbuf_cluster = memalign(ARCH_DMA_MINALIGN, MAX_CLUSTSIZE);
623 if (!tmpbuf_cluster)
624 return -1;
625 }
626
AKASHI Takahirob07f7042018-09-11 15:59:06 +0900627 assert(pos < bytesperclust);
628 startsect = clust_to_sect(mydata, clustnum);
629
630 debug("clustnum: %d, startsect: %d, pos: %lld\n",
631 clustnum, startsect, pos);
632
633 /* partial write at beginning */
634 if (pos) {
635 wsize = min(bytesperclust - pos, size);
636 ret = disk_read(startsect, mydata->clust_size, tmpbuf_cluster);
637 if (ret != mydata->clust_size) {
638 debug("Error reading data (got %d)\n", ret);
639 return -1;
640 }
641
642 memcpy(tmpbuf_cluster + pos, buffer, wsize);
643 ret = disk_write(startsect, mydata->clust_size, tmpbuf_cluster);
644 if (ret != mydata->clust_size) {
645 debug("Error writing data (got %d)\n", ret);
646 return -1;
647 }
648
649 size -= wsize;
650 buffer += wsize;
651 *gotsize += wsize;
652
653 startsect += mydata->clust_size;
654
655 if (!size)
656 return 0;
657 }
658
659 /* full-cluster write */
660 if (size >= bytesperclust) {
661 clustcount = lldiv(size, bytesperclust);
662
663 if (!((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1))) {
664 wsize = clustcount * bytesperclust;
665 ret = disk_write(startsect,
666 clustcount * mydata->clust_size,
667 buffer);
668 if (ret != clustcount * mydata->clust_size) {
669 debug("Error writing data (got %d)\n", ret);
670 return -1;
671 }
672
673 size -= wsize;
674 buffer += wsize;
675 *gotsize += wsize;
676
677 startsect += clustcount * mydata->clust_size;
678 } else {
679 for (i = 0; i < clustcount; i++) {
680 memcpy(tmpbuf_cluster, buffer, bytesperclust);
681 ret = disk_write(startsect,
682 mydata->clust_size,
683 tmpbuf_cluster);
684 if (ret != mydata->clust_size) {
685 debug("Error writing data (got %d)\n",
686 ret);
687 return -1;
688 }
689
690 size -= bytesperclust;
691 buffer += bytesperclust;
692 *gotsize += bytesperclust;
693
694 startsect += mydata->clust_size;
695 }
696 }
697 }
698
699 /* partial write at end */
700 if (size) {
701 wsize = size;
702 ret = disk_read(startsect, mydata->clust_size, tmpbuf_cluster);
703 if (ret != mydata->clust_size) {
704 debug("Error reading data (got %d)\n", ret);
705 return -1;
706 }
707 memcpy(tmpbuf_cluster, buffer, wsize);
708 ret = disk_write(startsect, mydata->clust_size, tmpbuf_cluster);
709 if (ret != mydata->clust_size) {
710 debug("Error writing data (got %d)\n", ret);
711 return -1;
712 }
713
714 size -= wsize;
715 buffer += wsize;
716 *gotsize += wsize;
717 }
718
719 assert(!size);
720
721 return 0;
722}
723
Donggeun Kim8f814002011-10-24 21:15:28 +0000724/*
725 * Find the first empty cluster
726 */
727static int find_empty_cluster(fsdata *mydata)
728{
729 __u32 fat_val, entry = 3;
730
731 while (1) {
Stefan Brüns9f95d812016-12-17 00:27:51 +0100732 fat_val = get_fatent(mydata, entry);
Donggeun Kim8f814002011-10-24 21:15:28 +0000733 if (fat_val == 0)
734 break;
735 entry++;
736 }
737
738 return entry;
739}
740
741/*
AKASHI Takahiroa2c0bd02019-05-24 14:10:36 +0900742 * Allocate a cluster for additional directory entries
Donggeun Kim8f814002011-10-24 21:15:28 +0000743 */
AKASHI Takahiroa2c0bd02019-05-24 14:10:36 +0900744static int new_dir_table(fat_itr *itr)
Donggeun Kim8f814002011-10-24 21:15:28 +0000745{
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +0900746 fsdata *mydata = itr->fsdata;
Donggeun Kim8f814002011-10-24 21:15:28 +0000747 int dir_newclust = 0;
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +0900748 unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
Donggeun Kim8f814002011-10-24 21:15:28 +0000749
Donggeun Kim8f814002011-10-24 21:15:28 +0000750 dir_newclust = find_empty_cluster(mydata);
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +0900751 set_fatent_value(mydata, itr->clust, dir_newclust);
Donggeun Kim8f814002011-10-24 21:15:28 +0000752 if (mydata->fatsize == 32)
753 set_fatent_value(mydata, dir_newclust, 0xffffff8);
754 else if (mydata->fatsize == 16)
755 set_fatent_value(mydata, dir_newclust, 0xfff8);
Philipp Skadorov16f553d2016-12-15 15:52:53 -0500756 else if (mydata->fatsize == 12)
757 set_fatent_value(mydata, dir_newclust, 0xff8);
Donggeun Kim8f814002011-10-24 21:15:28 +0000758
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +0900759 itr->clust = dir_newclust;
760 itr->next_clust = dir_newclust;
Donggeun Kim8f814002011-10-24 21:15:28 +0000761
Stefan Brüns751b31d2016-09-11 22:51:40 +0200762 if (flush_dirty_fat_buffer(mydata) < 0)
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +0900763 return -1;
764
765 memset(itr->block, 0x00, bytesperclust);
Donggeun Kim8f814002011-10-24 21:15:28 +0000766
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +0900767 itr->dent = (dir_entry *)itr->block;
768 itr->last_cluster = 1;
769 itr->remaining = bytesperclust / sizeof(dir_entry) - 1;
Donggeun Kim8f814002011-10-24 21:15:28 +0000770
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +0900771 return 0;
Donggeun Kim8f814002011-10-24 21:15:28 +0000772}
773
774/*
775 * Set empty cluster from 'entry' to the end of a file
776 */
777static int clear_fatent(fsdata *mydata, __u32 entry)
778{
779 __u32 fat_val;
780
Philipp Skadorov16f553d2016-12-15 15:52:53 -0500781 while (!CHECK_CLUST(entry, mydata->fatsize)) {
Stefan Brüns9f95d812016-12-17 00:27:51 +0100782 fat_val = get_fatent(mydata, entry);
Donggeun Kim8f814002011-10-24 21:15:28 +0000783 if (fat_val != 0)
784 set_fatent_value(mydata, entry, 0);
785 else
786 break;
787
Donggeun Kim8f814002011-10-24 21:15:28 +0000788 entry = fat_val;
789 }
790
791 /* Flush fat buffer */
Stefan Brüns751b31d2016-09-11 22:51:40 +0200792 if (flush_dirty_fat_buffer(mydata) < 0)
Donggeun Kim8f814002011-10-24 21:15:28 +0000793 return -1;
794
795 return 0;
796}
797
798/*
AKASHI Takahiroed8b1e42018-09-11 15:59:05 +0900799 * Set start cluster in directory entry
800 */
801static void set_start_cluster(const fsdata *mydata, dir_entry *dentptr,
802 __u32 start_cluster)
803{
804 if (mydata->fatsize == 32)
805 dentptr->starthi =
806 cpu_to_le16((start_cluster & 0xffff0000) >> 16);
807 dentptr->start = cpu_to_le16(start_cluster & 0xffff);
808}
809
810/*
811 * Check whether adding a file makes the file system to
812 * exceed the size of the block device
813 * Return -1 when overflow occurs, otherwise return 0
814 */
815static int check_overflow(fsdata *mydata, __u32 clustnum, loff_t size)
816{
817 __u32 startsect, sect_num, offset;
818
819 if (clustnum > 0)
820 startsect = clust_to_sect(mydata, clustnum);
821 else
822 startsect = mydata->rootdir_sect;
823
824 sect_num = div_u64_rem(size, mydata->sect_size, &offset);
825
826 if (offset != 0)
827 sect_num++;
828
829 if (startsect + sect_num > total_sector)
830 return -1;
831 return 0;
832}
833
834/*
Donggeun Kim8f814002011-10-24 21:15:28 +0000835 * Write at most 'maxsize' bytes from 'buffer' into
836 * the file associated with 'dentptr'
Suriyan Ramasami441c2232014-11-17 14:39:35 -0800837 * Update the number of bytes written in *gotsize and return 0
838 * or return -1 on fatal errors.
Donggeun Kim8f814002011-10-24 21:15:28 +0000839 */
840static int
AKASHI Takahiroed8b1e42018-09-11 15:59:05 +0900841set_contents(fsdata *mydata, dir_entry *dentptr, loff_t pos, __u8 *buffer,
842 loff_t maxsize, loff_t *gotsize)
Donggeun Kim8f814002011-10-24 21:15:28 +0000843{
Donggeun Kim8f814002011-10-24 21:15:28 +0000844 unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
845 __u32 curclust = START(dentptr);
846 __u32 endclust = 0, newclust = 0;
Heinrich Schuchardtdb538a92019-02-25 19:42:48 +0100847 u64 cur_pos, filesize;
848 loff_t offset, actsize, wsize;
Donggeun Kim8f814002011-10-24 21:15:28 +0000849
Suriyan Ramasami441c2232014-11-17 14:39:35 -0800850 *gotsize = 0;
AKASHI Takahirob07f7042018-09-11 15:59:06 +0900851 filesize = pos + maxsize;
Donggeun Kim8f814002011-10-24 21:15:28 +0000852
Suriyan Ramasami441c2232014-11-17 14:39:35 -0800853 debug("%llu bytes\n", filesize);
Donggeun Kim8f814002011-10-24 21:15:28 +0000854
AKASHI Takahirob07f7042018-09-11 15:59:06 +0900855 if (!filesize) {
856 if (!curclust)
857 return 0;
858 if (!CHECK_CLUST(curclust, mydata->fatsize) ||
859 IS_LAST_CLUST(curclust, mydata->fatsize)) {
860 clear_fatent(mydata, curclust);
861 set_start_cluster(mydata, dentptr, 0);
862 return 0;
863 }
864 debug("curclust: 0x%x\n", curclust);
865 debug("Invalid FAT entry\n");
866 return -1;
867 }
868
869 if (!curclust) {
870 assert(pos == 0);
871 goto set_clusters;
872 }
873
874 /* go to cluster at pos */
875 cur_pos = bytesperclust;
876 while (1) {
877 if (pos <= cur_pos)
878 break;
879 if (IS_LAST_CLUST(curclust, mydata->fatsize))
880 break;
881
882 newclust = get_fatent(mydata, curclust);
883 if (!IS_LAST_CLUST(newclust, mydata->fatsize) &&
884 CHECK_CLUST(newclust, mydata->fatsize)) {
885 debug("curclust: 0x%x\n", curclust);
886 debug("Invalid FAT entry\n");
Benoît Thébaudeau8fed7d32015-09-28 15:45:32 +0200887 return -1;
888 }
AKASHI Takahirob07f7042018-09-11 15:59:06 +0900889
890 cur_pos += bytesperclust;
891 curclust = newclust;
892 }
893 if (IS_LAST_CLUST(curclust, mydata->fatsize)) {
894 assert(pos == cur_pos);
895 goto set_clusters;
896 }
897
898 assert(pos < cur_pos);
899 cur_pos -= bytesperclust;
900
901 /* overwrite */
902 assert(IS_LAST_CLUST(curclust, mydata->fatsize) ||
903 !CHECK_CLUST(curclust, mydata->fatsize));
904
905 while (1) {
906 /* search for allocated consecutive clusters */
907 actsize = bytesperclust;
908 endclust = curclust;
909 while (1) {
910 if (filesize <= (cur_pos + actsize))
911 break;
912
913 newclust = get_fatent(mydata, endclust);
914
Marek Szyprowski2f241672019-12-02 12:11:13 +0100915 if (newclust != endclust + 1)
916 break;
AKASHI Takahirob07f7042018-09-11 15:59:06 +0900917 if (IS_LAST_CLUST(newclust, mydata->fatsize))
918 break;
919 if (CHECK_CLUST(newclust, mydata->fatsize)) {
920 debug("curclust: 0x%x\n", curclust);
921 debug("Invalid FAT entry\n");
922 return -1;
923 }
924
925 actsize += bytesperclust;
926 endclust = newclust;
927 }
928
929 /* overwrite to <curclust..endclust> */
930 if (pos < cur_pos)
931 offset = 0;
932 else
933 offset = pos - cur_pos;
Marek Szyprowski3a867ce2019-12-02 12:11:14 +0100934 wsize = min_t(unsigned long long, actsize, filesize - cur_pos);
935 wsize -= offset;
936
AKASHI Takahirob07f7042018-09-11 15:59:06 +0900937 if (get_set_cluster(mydata, curclust, offset,
938 buffer, wsize, &actsize)) {
939 printf("Error get-and-setting cluster\n");
940 return -1;
941 }
942 buffer += wsize;
943 *gotsize += wsize;
944 cur_pos += offset + wsize;
945
946 if (filesize <= cur_pos)
947 break;
948
AKASHI Takahirob07f7042018-09-11 15:59:06 +0900949 if (IS_LAST_CLUST(newclust, mydata->fatsize))
950 /* no more clusters */
951 break;
952
953 curclust = newclust;
954 }
955
956 if (filesize <= cur_pos) {
957 /* no more write */
958 newclust = get_fatent(mydata, endclust);
959 if (!IS_LAST_CLUST(newclust, mydata->fatsize)) {
960 /* truncate the rest */
961 clear_fatent(mydata, newclust);
962
963 /* Mark end of file in FAT */
964 if (mydata->fatsize == 12)
965 newclust = 0xfff;
966 else if (mydata->fatsize == 16)
967 newclust = 0xffff;
968 else if (mydata->fatsize == 32)
969 newclust = 0xfffffff;
970 set_fatent_value(mydata, endclust, newclust);
971 }
972
973 return 0;
AKASHI Takahiroed8b1e42018-09-11 15:59:05 +0900974 }
975
AKASHI Takahirob07f7042018-09-11 15:59:06 +0900976 curclust = endclust;
977 filesize -= cur_pos;
Heinrich Schuchardtdb538a92019-02-25 19:42:48 +0100978 assert(!do_div(cur_pos, bytesperclust));
AKASHI Takahiroed8b1e42018-09-11 15:59:05 +0900979
AKASHI Takahirob07f7042018-09-11 15:59:06 +0900980set_clusters:
981 /* allocate and write */
982 assert(!pos);
983
984 /* Assure that curclust is valid */
985 if (!curclust) {
986 curclust = find_empty_cluster(mydata);
987 set_start_cluster(mydata, dentptr, curclust);
988 } else {
989 newclust = get_fatent(mydata, curclust);
990
991 if (IS_LAST_CLUST(newclust, mydata->fatsize)) {
992 newclust = determine_fatent(mydata, curclust);
993 set_fatent_value(mydata, curclust, newclust);
994 curclust = newclust;
995 } else {
996 debug("error: something wrong\n");
997 return -1;
998 }
999 }
1000
1001 /* TODO: already partially written */
AKASHI Takahiroed8b1e42018-09-11 15:59:05 +09001002 if (check_overflow(mydata, curclust, filesize)) {
1003 printf("Error: no space left: %llu\n", filesize);
1004 return -1;
Benoît Thébaudeau8fed7d32015-09-28 15:45:32 +02001005 }
1006
Donggeun Kim8f814002011-10-24 21:15:28 +00001007 actsize = bytesperclust;
1008 endclust = curclust;
1009 do {
1010 /* search for consecutive clusters */
1011 while (actsize < filesize) {
1012 newclust = determine_fatent(mydata, endclust);
1013
1014 if ((newclust - 1) != endclust)
AKASHI Takahiroed8b1e42018-09-11 15:59:05 +09001015 /* write to <curclust..endclust> */
Donggeun Kim8f814002011-10-24 21:15:28 +00001016 goto getit;
1017
1018 if (CHECK_CLUST(newclust, mydata->fatsize)) {
Benoît Thébaudeaue0b86942015-09-28 15:45:30 +02001019 debug("newclust: 0x%x\n", newclust);
Donggeun Kim8f814002011-10-24 21:15:28 +00001020 debug("Invalid FAT entry\n");
Suriyan Ramasami441c2232014-11-17 14:39:35 -08001021 return 0;
Donggeun Kim8f814002011-10-24 21:15:28 +00001022 }
1023 endclust = newclust;
1024 actsize += bytesperclust;
1025 }
Donggeun Kim8f814002011-10-24 21:15:28 +00001026
1027 /* set remaining bytes */
Donggeun Kim8f814002011-10-24 21:15:28 +00001028 actsize = filesize;
Heinrich Schuchardt9fb8db42018-10-02 09:30:45 +02001029 if (set_cluster(mydata, curclust, buffer, (u32)actsize) != 0) {
Donggeun Kim8f814002011-10-24 21:15:28 +00001030 debug("error: writing cluster\n");
1031 return -1;
1032 }
Suriyan Ramasami441c2232014-11-17 14:39:35 -08001033 *gotsize += actsize;
Donggeun Kim8f814002011-10-24 21:15:28 +00001034
1035 /* Mark end of file in FAT */
Philipp Skadorov16f553d2016-12-15 15:52:53 -05001036 if (mydata->fatsize == 12)
1037 newclust = 0xfff;
1038 else if (mydata->fatsize == 16)
Donggeun Kim8f814002011-10-24 21:15:28 +00001039 newclust = 0xffff;
1040 else if (mydata->fatsize == 32)
1041 newclust = 0xfffffff;
1042 set_fatent_value(mydata, endclust, newclust);
1043
Suriyan Ramasami441c2232014-11-17 14:39:35 -08001044 return 0;
Donggeun Kim8f814002011-10-24 21:15:28 +00001045getit:
Heinrich Schuchardt9fb8db42018-10-02 09:30:45 +02001046 if (set_cluster(mydata, curclust, buffer, (u32)actsize) != 0) {
Donggeun Kim8f814002011-10-24 21:15:28 +00001047 debug("error: writing cluster\n");
1048 return -1;
1049 }
Suriyan Ramasami441c2232014-11-17 14:39:35 -08001050 *gotsize += actsize;
Donggeun Kim8f814002011-10-24 21:15:28 +00001051 filesize -= actsize;
1052 buffer += actsize;
1053
Benoît Thébaudeaue0b86942015-09-28 15:45:30 +02001054 if (CHECK_CLUST(newclust, mydata->fatsize)) {
1055 debug("newclust: 0x%x\n", newclust);
Donggeun Kim8f814002011-10-24 21:15:28 +00001056 debug("Invalid FAT entry\n");
Suriyan Ramasami441c2232014-11-17 14:39:35 -08001057 return 0;
Donggeun Kim8f814002011-10-24 21:15:28 +00001058 }
1059 actsize = bytesperclust;
1060 curclust = endclust = newclust;
1061 } while (1);
Donggeun Kim8f814002011-10-24 21:15:28 +00001062
AKASHI Takahiroed8b1e42018-09-11 15:59:05 +09001063 return 0;
Benoît Thébaudeau8fed7d32015-09-28 15:45:32 +02001064}
1065
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +01001066/**
1067 * fill_dentry() - fill directory entry with shortname
1068 *
1069 * @mydata: private filesystem parameters
1070 * @dentptr: directory entry
1071 * @shortname: chosen short name
1072 * @start_cluster: first cluster of file
1073 * @size: file size
1074 * @attr: file attributes
Benoît Thébaudeau8fed7d32015-09-28 15:45:32 +02001075 */
1076static void fill_dentry(fsdata *mydata, dir_entry *dentptr,
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +01001077 const char *shortname, __u32 start_cluster, __u32 size, __u8 attr)
Benoît Thébaudeau8fed7d32015-09-28 15:45:32 +02001078{
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +01001079 memset(dentptr, 0, sizeof(*dentptr));
1080
Benoît Thébaudeau8fed7d32015-09-28 15:45:32 +02001081 set_start_cluster(mydata, dentptr, start_cluster);
Donggeun Kim8f814002011-10-24 21:15:28 +00001082 dentptr->size = cpu_to_le32(size);
1083
1084 dentptr->attr = attr;
1085
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +01001086 memcpy(dentptr->name, shortname, SHORT_NAME_SIZE);
Donggeun Kim8f814002011-10-24 21:15:28 +00001087}
1088
1089/*
Donggeun Kim8f814002011-10-24 21:15:28 +00001090 * Find a directory entry based on filename or start cluster number
1091 * If the directory entry is not found,
1092 * the new position for writing a directory entry will be returned
1093 */
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001094static dir_entry *find_directory_entry(fat_itr *itr, char *filename)
Donggeun Kim8f814002011-10-24 21:15:28 +00001095{
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001096 int match = 0;
Donggeun Kim8f814002011-10-24 21:15:28 +00001097
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001098 while (fat_itr_next(itr)) {
1099 /* check both long and short name: */
1100 if (!strcasecmp(filename, itr->name))
1101 match = 1;
1102 else if (itr->name != itr->s_name &&
1103 !strcasecmp(filename, itr->s_name))
1104 match = 1;
Donggeun Kim8f814002011-10-24 21:15:28 +00001105
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001106 if (!match)
1107 continue;
Donggeun Kim8f814002011-10-24 21:15:28 +00001108
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001109 if (itr->dent->name[0] == '\0')
Donggeun Kim8f814002011-10-24 21:15:28 +00001110 return NULL;
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001111 else
1112 return itr->dent;
1113 }
Donggeun Kim8f814002011-10-24 21:15:28 +00001114
AKASHI Takahiroc3269332019-05-24 14:10:37 +09001115 /* allocate a cluster for more entries */
1116 if (!itr->dent &&
1117 (!itr->is_root || itr->fsdata->fatsize == 32) &&
1118 new_dir_table(itr))
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001119 /* indicate that allocating dent failed */
1120 itr->dent = NULL;
Donggeun Kim8f814002011-10-24 21:15:28 +00001121
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001122 return NULL;
1123}
Donggeun Kim8f814002011-10-24 21:15:28 +00001124
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001125static int split_filename(char *filename, char **dirname, char **basename)
1126{
1127 char *p, *last_slash, *last_slash_cont;
Donggeun Kim8f814002011-10-24 21:15:28 +00001128
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001129again:
1130 p = filename;
1131 last_slash = NULL;
1132 last_slash_cont = NULL;
1133 while (*p) {
1134 if (ISDIRDELIM(*p)) {
1135 last_slash = p;
1136 last_slash_cont = p;
1137 /* continuous slashes */
1138 while (ISDIRDELIM(*p))
1139 last_slash_cont = p++;
1140 if (!*p)
1141 break;
Donggeun Kim8f814002011-10-24 21:15:28 +00001142 }
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001143 p++;
1144 }
Wu, Josh675b23c2014-05-08 16:14:07 +08001145
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001146 if (last_slash) {
1147 if (last_slash_cont == (filename + strlen(filename) - 1)) {
1148 /* remove trailing slashes */
1149 *last_slash = '\0';
1150 goto again;
Wu, Josh675b23c2014-05-08 16:14:07 +08001151 }
1152
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001153 if (last_slash == filename) {
1154 /* avoid ""(null) directory */
1155 *dirname = "/";
1156 } else {
1157 *last_slash = '\0';
1158 *dirname = filename;
Donggeun Kim8f814002011-10-24 21:15:28 +00001159 }
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001160
1161 *last_slash_cont = '\0';
1162 *basename = last_slash_cont + 1;
1163 } else {
1164 *dirname = "/"; /* root by default */
1165 *basename = filename;
Donggeun Kim8f814002011-10-24 21:15:28 +00001166 }
1167
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001168 return 0;
Donggeun Kim8f814002011-10-24 21:15:28 +00001169}
1170
Heinrich Schuchardt58587212019-05-12 09:59:18 +02001171/**
1172 * normalize_longname() - check long file name and convert to lower case
1173 *
1174 * We assume here that the FAT file system is using an 8bit code page.
1175 * Linux typically uses CP437, EDK2 assumes CP1250.
1176 *
1177 * @l_filename: preallocated buffer receiving the normalized name
1178 * @filename: filename to normalize
1179 * Return: 0 on success, -1 on failure
1180 */
AKASHI Takahiroc83df1a2018-09-11 15:59:02 +09001181static int normalize_longname(char *l_filename, const char *filename)
1182{
Heinrich Schuchardt58587212019-05-12 09:59:18 +02001183 const char *p, illegal[] = "<>:\"/\\|?*";
AKASHI Takahiroc83df1a2018-09-11 15:59:02 +09001184
Heinrich Schuchardt58587212019-05-12 09:59:18 +02001185 if (strlen(filename) >= VFAT_MAXLEN_BYTES)
AKASHI Takahiroc83df1a2018-09-11 15:59:02 +09001186 return -1;
AKASHI Takahiroc83df1a2018-09-11 15:59:02 +09001187
Heinrich Schuchardt58587212019-05-12 09:59:18 +02001188 for (p = filename; *p; ++p) {
1189 if ((unsigned char)*p < 0x20)
1190 return -1;
1191 if (strchr(illegal, *p))
1192 return -1;
1193 }
AKASHI Takahiroc83df1a2018-09-11 15:59:02 +09001194
Heinrich Schuchardt58587212019-05-12 09:59:18 +02001195 strcpy(l_filename, filename);
1196 downcase(l_filename, VFAT_MAXLEN_BYTES);
AKASHI Takahiroc83df1a2018-09-11 15:59:02 +09001197
1198 return 0;
1199}
1200
AKASHI Takahiroed8b1e42018-09-11 15:59:05 +09001201int file_fat_write_at(const char *filename, loff_t pos, void *buffer,
1202 loff_t size, loff_t *actwrite)
Donggeun Kim8f814002011-10-24 21:15:28 +00001203{
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001204 dir_entry *retdent;
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001205 fsdata datablock = { .fatbuf = NULL, };
Donggeun Kim8f814002011-10-24 21:15:28 +00001206 fsdata *mydata = &datablock;
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001207 fat_itr *itr = NULL;
AKASHI Takahiroc83df1a2018-09-11 15:59:02 +09001208 int ret = -1;
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001209 char *filename_copy, *parent, *basename;
Donggeun Kim8f814002011-10-24 21:15:28 +00001210 char l_filename[VFAT_MAXLEN_BYTES];
1211
AKASHI Takahiroed8b1e42018-09-11 15:59:05 +09001212 debug("writing %s\n", filename);
1213
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001214 filename_copy = strdup(filename);
1215 if (!filename_copy)
1216 return -ENOMEM;
Donggeun Kim8f814002011-10-24 21:15:28 +00001217
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001218 split_filename(filename_copy, &parent, &basename);
1219 if (!strlen(basename)) {
1220 ret = -EINVAL;
1221 goto exit;
Donggeun Kim8f814002011-10-24 21:15:28 +00001222 }
1223
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001224 filename = basename;
1225 if (normalize_longname(l_filename, filename)) {
1226 printf("FAT: illegal filename (%s)\n", filename);
1227 ret = -EINVAL;
1228 goto exit;
Donggeun Kim8f814002011-10-24 21:15:28 +00001229 }
1230
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001231 itr = malloc_cache_aligned(sizeof(fat_itr));
1232 if (!itr) {
1233 ret = -ENOMEM;
1234 goto exit;
Donggeun Kim8f814002011-10-24 21:15:28 +00001235 }
1236
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001237 ret = fat_itr_root(itr, &datablock);
1238 if (ret)
Donggeun Kim8f814002011-10-24 21:15:28 +00001239 goto exit;
Donggeun Kim8f814002011-10-24 21:15:28 +00001240
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001241 total_sector = datablock.total_sect;
1242
1243 ret = fat_itr_resolve(itr, parent, TYPE_DIR);
1244 if (ret) {
1245 printf("%s: doesn't exist (%d)\n", parent, ret);
AKASHI Takahiroc83df1a2018-09-11 15:59:02 +09001246 goto exit;
1247 }
Donggeun Kim8f814002011-10-24 21:15:28 +00001248
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001249 retdent = find_directory_entry(itr, l_filename);
1250
Donggeun Kim8f814002011-10-24 21:15:28 +00001251 if (retdent) {
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001252 if (fat_itr_isdir(itr)) {
1253 ret = -EISDIR;
1254 goto exit;
1255 }
1256
AKASHI Takahirob07f7042018-09-11 15:59:06 +09001257 /* A file exists */
1258 if (pos == -1)
1259 /* Append to the end */
1260 pos = FAT2CPU32(retdent->size);
1261 if (pos > retdent->size) {
1262 /* No hole allowed */
1263 ret = -EINVAL;
1264 goto exit;
1265 }
1266
AKASHI Takahiroed8b1e42018-09-11 15:59:05 +09001267 /* Update file size in a directory entry */
1268 retdent->size = cpu_to_le32(pos + size);
Donggeun Kim8f814002011-10-24 21:15:28 +00001269 } else {
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001270 /* Create a new file */
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +01001271 char shortname[SHORT_NAME_SIZE];
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001272
1273 if (itr->is_root) {
1274 /* root dir cannot have "." or ".." */
1275 if (!strcmp(l_filename, ".") ||
1276 !strcmp(l_filename, "..")) {
1277 ret = -EINVAL;
1278 goto exit;
1279 }
1280 }
1281
1282 if (!itr->dent) {
1283 printf("Error: allocating new dir entry\n");
1284 ret = -EIO;
1285 goto exit;
1286 }
1287
AKASHI Takahirob07f7042018-09-11 15:59:06 +09001288 if (pos) {
1289 /* No hole allowed */
1290 ret = -EINVAL;
1291 goto exit;
1292 }
1293
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +01001294 /* Check if long name is needed */
Heinrich Schuchardt24977e52020-11-25 16:33:55 +01001295 ret = set_name(itr, filename, shortname);
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +01001296 if (ret < 0)
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001297 goto exit;
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +01001298 if (ret > 1) {
1299 /* Set long name entries */
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +01001300 ret = fill_dir_slot(itr, filename, shortname);
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +01001301 if (ret)
1302 goto exit;
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001303 }
Donggeun Kim8f814002011-10-24 21:15:28 +00001304
AKASHI Takahiroa2c0bd02019-05-24 14:10:36 +09001305 /* Set short name entry */
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +01001306 fill_dentry(itr->fsdata, itr->dent, shortname, 0, size,
Heinrich Schuchardt881c74a2020-11-22 11:13:33 +01001307 ATTR_ARCH);
Donggeun Kim8f814002011-10-24 21:15:28 +00001308
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001309 retdent = itr->dent;
Benoît Thébaudeaud1d390a2015-09-28 15:45:31 +02001310 }
Donggeun Kim8f814002011-10-24 21:15:28 +00001311
AKASHI Takahiroed8b1e42018-09-11 15:59:05 +09001312 ret = set_contents(mydata, retdent, pos, buffer, size, actwrite);
Benoît Thébaudeaud1d390a2015-09-28 15:45:31 +02001313 if (ret < 0) {
1314 printf("Error: writing contents\n");
AKASHI Takahiro6aa77692018-09-11 15:59:03 +09001315 ret = -EIO;
Benoît Thébaudeaud1d390a2015-09-28 15:45:31 +02001316 goto exit;
1317 }
1318 debug("attempt to write 0x%llx bytes\n", *actwrite);
Donggeun Kim8f814002011-10-24 21:15:28 +00001319
Benoît Thébaudeaud1d390a2015-09-28 15:45:31 +02001320 /* Flush fat buffer */
Stefan Brüns751b31d2016-09-11 22:51:40 +02001321 ret = flush_dirty_fat_buffer(mydata);
Benoît Thébaudeaud1d390a2015-09-28 15:45:31 +02001322 if (ret) {
1323 printf("Error: flush fat buffer\n");
AKASHI Takahiro6aa77692018-09-11 15:59:03 +09001324 ret = -EIO;
Benoît Thébaudeaud1d390a2015-09-28 15:45:31 +02001325 goto exit;
Donggeun Kim8f814002011-10-24 21:15:28 +00001326 }
1327
Benoît Thébaudeaud1d390a2015-09-28 15:45:31 +02001328 /* Write directory table to device */
AKASHI Takahirod98c6742019-05-24 14:10:35 +09001329 ret = flush_dir(itr);
AKASHI Takahiro6aa77692018-09-11 15:59:03 +09001330 if (ret) {
Benoît Thébaudeaud1d390a2015-09-28 15:45:31 +02001331 printf("Error: writing directory entry\n");
AKASHI Takahiro6aa77692018-09-11 15:59:03 +09001332 ret = -EIO;
1333 }
Benoît Thébaudeaud1d390a2015-09-28 15:45:31 +02001334
Donggeun Kim8f814002011-10-24 21:15:28 +00001335exit:
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001336 free(filename_copy);
Donggeun Kim8f814002011-10-24 21:15:28 +00001337 free(mydata->fatbuf);
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001338 free(itr);
Suriyan Ramasami441c2232014-11-17 14:39:35 -08001339 return ret;
Donggeun Kim8f814002011-10-24 21:15:28 +00001340}
1341
Suriyan Ramasami441c2232014-11-17 14:39:35 -08001342int file_fat_write(const char *filename, void *buffer, loff_t offset,
1343 loff_t maxsize, loff_t *actwrite)
Donggeun Kim8f814002011-10-24 21:15:28 +00001344{
AKASHI Takahiroed8b1e42018-09-11 15:59:05 +09001345 return file_fat_write_at(filename, offset, buffer, maxsize, actwrite);
Donggeun Kim8f814002011-10-24 21:15:28 +00001346}
AKASHI Takahiro1c24b7b2018-09-11 15:59:10 +09001347
AKASHI Takahiro73b34972018-09-11 15:59:14 +09001348static int fat_dir_entries(fat_itr *itr)
1349{
1350 fat_itr *dirs;
1351 fsdata fsdata = { .fatbuf = NULL, }, *mydata = &fsdata;
1352 /* for FATBUFSIZE */
1353 int count;
1354
1355 dirs = malloc_cache_aligned(sizeof(fat_itr));
1356 if (!dirs) {
1357 debug("Error: allocating memory\n");
1358 count = -ENOMEM;
1359 goto exit;
1360 }
1361
1362 /* duplicate fsdata */
1363 fat_itr_child(dirs, itr);
1364 fsdata = *dirs->fsdata;
1365
1366 /* allocate local fat buffer */
1367 fsdata.fatbuf = malloc_cache_aligned(FATBUFSIZE);
1368 if (!fsdata.fatbuf) {
1369 debug("Error: allocating memory\n");
1370 count = -ENOMEM;
1371 goto exit;
1372 }
1373 fsdata.fatbufnum = -1;
1374 dirs->fsdata = &fsdata;
1375
1376 for (count = 0; fat_itr_next(dirs); count++)
1377 ;
1378
1379exit:
1380 free(fsdata.fatbuf);
1381 free(dirs);
1382 return count;
1383}
1384
1385static int delete_dentry(fat_itr *itr)
1386{
1387 fsdata *mydata = itr->fsdata;
1388 dir_entry *dentptr = itr->dent;
1389
1390 /* free cluster blocks */
1391 clear_fatent(mydata, START(dentptr));
1392 if (flush_dirty_fat_buffer(mydata) < 0) {
1393 printf("Error: flush fat buffer\n");
1394 return -EIO;
1395 }
1396
1397 /*
1398 * update a directory entry
1399 * TODO:
1400 * - long file name support
1401 * - find and mark the "new" first invalid entry as name[0]=0x00
1402 */
1403 memset(dentptr, 0, sizeof(*dentptr));
1404 dentptr->name[0] = 0xe5;
1405
AKASHI Takahirod98c6742019-05-24 14:10:35 +09001406 if (flush_dir(itr)) {
AKASHI Takahiro73b34972018-09-11 15:59:14 +09001407 printf("error: writing directory entry\n");
1408 return -EIO;
1409 }
1410
1411 return 0;
1412}
1413
1414int fat_unlink(const char *filename)
1415{
1416 fsdata fsdata = { .fatbuf = NULL, };
1417 fat_itr *itr = NULL;
1418 int n_entries, ret;
1419 char *filename_copy, *dirname, *basename;
1420
1421 filename_copy = strdup(filename);
Heinrich Schuchardtb2242f62018-10-02 06:58:00 +02001422 if (!filename_copy) {
1423 printf("Error: allocating memory\n");
1424 ret = -ENOMEM;
1425 goto exit;
1426 }
AKASHI Takahiro73b34972018-09-11 15:59:14 +09001427 split_filename(filename_copy, &dirname, &basename);
1428
1429 if (!strcmp(dirname, "/") && !strcmp(basename, "")) {
1430 printf("Error: cannot remove root\n");
1431 ret = -EINVAL;
1432 goto exit;
1433 }
1434
1435 itr = malloc_cache_aligned(sizeof(fat_itr));
1436 if (!itr) {
1437 printf("Error: allocating memory\n");
Heinrich Schuchardtb2242f62018-10-02 06:58:00 +02001438 ret = -ENOMEM;
1439 goto exit;
AKASHI Takahiro73b34972018-09-11 15:59:14 +09001440 }
1441
1442 ret = fat_itr_root(itr, &fsdata);
1443 if (ret)
1444 goto exit;
1445
1446 total_sector = fsdata.total_sect;
1447
1448 ret = fat_itr_resolve(itr, dirname, TYPE_DIR);
1449 if (ret) {
1450 printf("%s: doesn't exist (%d)\n", dirname, ret);
1451 ret = -ENOENT;
1452 goto exit;
1453 }
1454
1455 if (!find_directory_entry(itr, basename)) {
1456 printf("%s: doesn't exist\n", basename);
1457 ret = -ENOENT;
1458 goto exit;
1459 }
1460
1461 if (fat_itr_isdir(itr)) {
1462 n_entries = fat_dir_entries(itr);
1463 if (n_entries < 0) {
1464 ret = n_entries;
1465 goto exit;
1466 }
1467 if (n_entries > 2) {
1468 printf("Error: directory is not empty: %d\n",
1469 n_entries);
1470 ret = -EINVAL;
1471 goto exit;
1472 }
1473 }
1474
1475 ret = delete_dentry(itr);
1476
1477exit:
1478 free(fsdata.fatbuf);
1479 free(itr);
1480 free(filename_copy);
1481
1482 return ret;
1483}
1484
AKASHI Takahiro1c24b7b2018-09-11 15:59:10 +09001485int fat_mkdir(const char *new_dirname)
1486{
1487 dir_entry *retdent;
1488 fsdata datablock = { .fatbuf = NULL, };
1489 fsdata *mydata = &datablock;
1490 fat_itr *itr = NULL;
1491 char *dirname_copy, *parent, *dirname;
1492 char l_dirname[VFAT_MAXLEN_BYTES];
1493 int ret = -1;
1494 loff_t actwrite;
1495 unsigned int bytesperclust;
1496 dir_entry *dotdent = NULL;
1497
1498 dirname_copy = strdup(new_dirname);
1499 if (!dirname_copy)
1500 goto exit;
1501
1502 split_filename(dirname_copy, &parent, &dirname);
1503 if (!strlen(dirname)) {
1504 ret = -EINVAL;
1505 goto exit;
1506 }
1507
1508 if (normalize_longname(l_dirname, dirname)) {
1509 printf("FAT: illegal filename (%s)\n", dirname);
1510 ret = -EINVAL;
1511 goto exit;
1512 }
1513
1514 itr = malloc_cache_aligned(sizeof(fat_itr));
1515 if (!itr) {
1516 ret = -ENOMEM;
1517 goto exit;
1518 }
1519
1520 ret = fat_itr_root(itr, &datablock);
1521 if (ret)
1522 goto exit;
1523
1524 total_sector = datablock.total_sect;
1525
1526 ret = fat_itr_resolve(itr, parent, TYPE_DIR);
1527 if (ret) {
1528 printf("%s: doesn't exist (%d)\n", parent, ret);
1529 goto exit;
1530 }
1531
1532 retdent = find_directory_entry(itr, l_dirname);
1533
1534 if (retdent) {
1535 printf("%s: already exists\n", l_dirname);
1536 ret = -EEXIST;
1537 goto exit;
1538 } else {
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +01001539 char shortname[SHORT_NAME_SIZE];
1540
AKASHI Takahiro1c24b7b2018-09-11 15:59:10 +09001541 if (itr->is_root) {
1542 /* root dir cannot have "." or ".." */
1543 if (!strcmp(l_dirname, ".") ||
1544 !strcmp(l_dirname, "..")) {
1545 ret = -EINVAL;
1546 goto exit;
1547 }
1548 }
1549
1550 if (!itr->dent) {
1551 printf("Error: allocating new dir entry\n");
1552 ret = -EIO;
1553 goto exit;
1554 }
1555
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +01001556 /* Check if long name is needed */
Heinrich Schuchardt24977e52020-11-25 16:33:55 +01001557 ret = set_name(itr, dirname, shortname);
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +01001558 if (ret < 0)
1559 goto exit;
1560 if (ret > 1) {
1561 /* Set long name entries */
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +01001562 ret = fill_dir_slot(itr, dirname, shortname);
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +01001563 if (ret)
1564 goto exit;
1565 }
AKASHI Takahiro1c24b7b2018-09-11 15:59:10 +09001566
1567 /* Set attribute as archive for regular file */
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +01001568 fill_dentry(itr->fsdata, itr->dent, shortname, 0, 0,
AKASHI Takahiro1c24b7b2018-09-11 15:59:10 +09001569 ATTR_DIR | ATTR_ARCH);
1570
1571 retdent = itr->dent;
1572 }
1573
1574 /* Default entries */
1575 bytesperclust = mydata->clust_size * mydata->sect_size;
1576 dotdent = malloc_cache_aligned(bytesperclust);
1577 if (!dotdent) {
1578 ret = -ENOMEM;
1579 goto exit;
1580 }
1581 memset(dotdent, 0, bytesperclust);
1582
1583 memcpy(dotdent[0].name, ". ", 8);
1584 memcpy(dotdent[0].ext, " ", 3);
1585 dotdent[0].attr = ATTR_DIR | ATTR_ARCH;
1586
1587 memcpy(dotdent[1].name, ".. ", 8);
1588 memcpy(dotdent[1].ext, " ", 3);
1589 dotdent[1].attr = ATTR_DIR | ATTR_ARCH;
Heinrich Schuchardt00cf0762020-11-24 21:04:07 +01001590
1591 if (itr->is_root)
1592 set_start_cluster(mydata, &dotdent[1], 0);
1593 else
1594 set_start_cluster(mydata, &dotdent[1], itr->start_clust);
AKASHI Takahiro1c24b7b2018-09-11 15:59:10 +09001595
1596 ret = set_contents(mydata, retdent, 0, (__u8 *)dotdent,
1597 bytesperclust, &actwrite);
1598 if (ret < 0) {
1599 printf("Error: writing contents\n");
1600 goto exit;
1601 }
1602 /* Write twice for "." */
1603 set_start_cluster(mydata, &dotdent[0], START(retdent));
1604 ret = set_contents(mydata, retdent, 0, (__u8 *)dotdent,
1605 bytesperclust, &actwrite);
1606 if (ret < 0) {
1607 printf("Error: writing contents\n");
1608 goto exit;
1609 }
1610
1611 /* Flush fat buffer */
1612 ret = flush_dirty_fat_buffer(mydata);
1613 if (ret) {
1614 printf("Error: flush fat buffer\n");
1615 goto exit;
1616 }
1617
1618 /* Write directory table to device */
AKASHI Takahirod98c6742019-05-24 14:10:35 +09001619 ret = flush_dir(itr);
AKASHI Takahiro1c24b7b2018-09-11 15:59:10 +09001620 if (ret)
1621 printf("Error: writing directory entry\n");
1622
1623exit:
1624 free(dirname_copy);
1625 free(mydata->fatbuf);
1626 free(itr);
1627 free(dotdent);
1628 return ret;
1629}