blob: 716b6a6627d971a381c542da641164a1c2ce3ff5 [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 Schuchardtba9c44e2020-11-20 12:55:22 +010023/* Characters that may only be used in long file names */
24static const char LONG_ONLY_CHARS[] = "+,;=[]";
25
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +010026/* Combined size of the name and ext fields in the directory entry */
27#define SHORT_NAME_SIZE 11
28
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +010029/**
30 * str2fat() - convert string to valid FAT name characters
31 *
32 * Stop when reaching end of @src or a period.
33 * Ignore spaces.
34 * Replace characters that may only be used in long names by underscores.
35 * Convert lower case characters to upper case.
36 *
37 * To avoid assumptions about the code page we do not use characters
38 * above 0x7f for the short name.
39 *
40 * @dest: destination buffer
41 * @src: source buffer
42 * @length: size of destination buffer
43 * Return: number of bytes in destination buffer
44 */
45static int str2fat(char *dest, char *src, int length)
Donggeun Kim8f814002011-10-24 21:15:28 +000046{
47 int i;
48
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +010049 for (i = 0; i < length; ++src) {
50 char c = *src;
51
52 if (!c || c == '.')
53 break;
54 if (c == ' ')
55 continue;
56 if (strchr(LONG_ONLY_CHARS, c) || c > 0x7f)
57 c = '_';
58 else if (c >= 'a' && c <= 'z')
59 c &= 0xdf;
60 dest[i] = c;
61 ++i;
Donggeun Kim8f814002011-10-24 21:15:28 +000062 }
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +010063 return i;
Donggeun Kim8f814002011-10-24 21:15:28 +000064}
65
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +010066/**
67 * set_name() - set short name in directory entry
68 *
69 * The function determines if the @filename is a valid short name.
70 * In this case no long name is needed.
71 *
72 * If a long name is needed, a short name is constructed.
73 *
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +010074 * @filename: long file name
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +010075 * @shortname: buffer of 11 bytes to receive chosen short name and extension
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +010076 * Return: number of directory entries needed, negative on error
77 */
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +010078static int set_name(const char *filename, char *shortname)
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +010079{
80 char *period;
81 char *pos;
82 int period_location;
83 char buf[13];
84 int i;
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +010085 int ret;
86 struct {
87 char name[8];
88 char ext[3];
89 } dirent;
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +010090
91 if (!filename)
92 return -EIO;
93
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +010094 /* Initialize buffer */
95 memset(&dirent, ' ', sizeof(dirent));
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +010096
97 /* Convert filename to upper case short name */
98 period = strrchr(filename, '.');
99 pos = (char *)filename;
100 if (*pos == '.') {
101 pos = period + 1;
102 period = 0;
103 }
104 if (period)
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +0100105 str2fat(dirent.ext, period + 1, sizeof(dirent.ext));
106 period_location = str2fat(dirent.name, pos, sizeof(dirent.name));
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +0100107 if (period_location < 0)
108 return period_location;
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +0100109 if (*dirent.name == ' ')
110 *dirent.name = '_';
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +0100111 /* 0xe5 signals a deleted directory entry. Replace it by 0x05. */
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +0100112 if (*dirent.name == 0xe5)
113 *dirent.name = 0x05;
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +0100114
115 /* If filename and short name are the same, quit. */
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +0100116 sprintf(buf, "%.*s.%.3s", period_location, dirent.name, dirent.ext);
117 if (!strcmp(buf, filename)) {
118 ret = 1;
119 goto out;
120 }
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +0100121
122 /* Construct an indexed short name */
123 for (i = 1; i < 0x200000; ++i) {
124 int suffix_len;
125 int suffix_start;
126 int j;
127
128 /* To speed up the search use random numbers */
129 if (i < 10) {
130 j = i;
131 } else {
132 j = 30 - fls(i);
133 j = 10 + (rand() >> j);
134 }
135 sprintf(buf, "~%d", j);
136 suffix_len = strlen(buf);
137 suffix_start = 8 - suffix_len;
138 if (suffix_start > period_location)
139 suffix_start = period_location;
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +0100140 memcpy(dirent.name + suffix_start, buf, suffix_len);
141 if (*dirent.ext != ' ')
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +0100142 sprintf(buf, "%.*s.%.3s", suffix_start + suffix_len,
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +0100143 dirent.name, dirent.ext);
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +0100144 else
145 sprintf(buf, "%.*s", suffix_start + suffix_len,
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +0100146 dirent.name);
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +0100147 debug("short name: %s\n", buf);
148 /* TODO: Check that the short name does not exist yet. */
149
150 /* Each long name directory entry takes 13 characters. */
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +0100151 ret = (strlen(filename) + 25) / 13;
152 goto out;
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +0100153 }
154 return -EIO;
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +0100155out:
156 memcpy(shortname, dirent.name, SHORT_NAME_SIZE);
157 return ret;
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +0100158}
159
Donggeun Kim8f814002011-10-24 21:15:28 +0000160static int total_sector;
Donggeun Kimcb5f3bc2012-03-22 04:38:55 +0000161static int disk_write(__u32 block, __u32 nr_blocks, void *buf)
Donggeun Kim8f814002011-10-24 21:15:28 +0000162{
Łukasz Majewskid6f23d82015-09-03 14:21:39 +0200163 ulong ret;
164
Simon Glass2ee8ada2016-02-29 15:25:52 -0700165 if (!cur_dev)
Donggeun Kim8f814002011-10-24 21:15:28 +0000166 return -1;
167
Donggeun Kimcb5f3bc2012-03-22 04:38:55 +0000168 if (cur_part_info.start + block + nr_blocks >
169 cur_part_info.start + total_sector) {
Donggeun Kim8f814002011-10-24 21:15:28 +0000170 printf("error: overflow occurs\n");
171 return -1;
172 }
173
Simon Glass2ee8ada2016-02-29 15:25:52 -0700174 ret = blk_dwrite(cur_dev, cur_part_info.start + block, nr_blocks, buf);
Łukasz Majewskid6f23d82015-09-03 14:21:39 +0200175 if (nr_blocks && ret == 0)
176 return -1;
177
178 return ret;
Donggeun Kim8f814002011-10-24 21:15:28 +0000179}
180
Donggeun Kim8f814002011-10-24 21:15:28 +0000181/*
182 * Write fat buffer into block device
183 */
Stefan Brüns751b31d2016-09-11 22:51:40 +0200184static int flush_dirty_fat_buffer(fsdata *mydata)
Donggeun Kim8f814002011-10-24 21:15:28 +0000185{
186 int getsize = FATBUFBLOCKS;
187 __u32 fatlength = mydata->fatlength;
188 __u8 *bufptr = mydata->fatbuf;
189 __u32 startblock = mydata->fatbufnum * FATBUFBLOCKS;
190
Stefan Brüns751b31d2016-09-11 22:51:40 +0200191 debug("debug: evicting %d, dirty: %d\n", mydata->fatbufnum,
192 (int)mydata->fat_dirty);
193
194 if ((!mydata->fat_dirty) || (mydata->fatbufnum == -1))
195 return 0;
196
Stefan Brüns58bf86f2016-12-17 00:27:50 +0100197 /* Cap length if fatlength is not a multiple of FATBUFBLOCKS */
198 if (startblock + getsize > fatlength)
199 getsize = fatlength - startblock;
Donggeun Kim8f814002011-10-24 21:15:28 +0000200
Stefan Brüns58bf86f2016-12-17 00:27:50 +0100201 startblock += mydata->fat_sect;
Donggeun Kim8f814002011-10-24 21:15:28 +0000202
203 /* Write FAT buf */
204 if (disk_write(startblock, getsize, bufptr) < 0) {
205 debug("error: writing FAT blocks\n");
206 return -1;
207 }
208
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +0900209 if (mydata->fats == 2) {
Donggeun Kimaf57ce22011-12-20 18:34:27 +0000210 /* Update corresponding second FAT blocks */
211 startblock += mydata->fatlength;
212 if (disk_write(startblock, getsize, bufptr) < 0) {
213 debug("error: writing second FAT blocks\n");
214 return -1;
215 }
216 }
Stefan Brüns751b31d2016-09-11 22:51:40 +0200217 mydata->fat_dirty = 0;
Donggeun Kimaf57ce22011-12-20 18:34:27 +0000218
Donggeun Kim8f814002011-10-24 21:15:28 +0000219 return 0;
220}
221
222/*
Donggeun Kim8f814002011-10-24 21:15:28 +0000223 * Set the file name information from 'name' into 'slotptr',
224 */
225static int str2slot(dir_slot *slotptr, const char *name, int *idx)
226{
227 int j, end_idx = 0;
228
229 for (j = 0; j <= 8; j += 2) {
230 if (name[*idx] == 0x00) {
231 slotptr->name0_4[j] = 0;
232 slotptr->name0_4[j + 1] = 0;
233 end_idx++;
234 goto name0_4;
235 }
236 slotptr->name0_4[j] = name[*idx];
237 (*idx)++;
238 end_idx++;
239 }
240 for (j = 0; j <= 10; j += 2) {
241 if (name[*idx] == 0x00) {
242 slotptr->name5_10[j] = 0;
243 slotptr->name5_10[j + 1] = 0;
244 end_idx++;
245 goto name5_10;
246 }
247 slotptr->name5_10[j] = name[*idx];
248 (*idx)++;
249 end_idx++;
250 }
251 for (j = 0; j <= 2; j += 2) {
252 if (name[*idx] == 0x00) {
253 slotptr->name11_12[j] = 0;
254 slotptr->name11_12[j + 1] = 0;
255 end_idx++;
256 goto name11_12;
257 }
258 slotptr->name11_12[j] = name[*idx];
259 (*idx)++;
260 end_idx++;
261 }
262
263 if (name[*idx] == 0x00)
264 return 1;
265
266 return 0;
267/* Not used characters are filled with 0xff 0xff */
268name0_4:
269 for (; end_idx < 5; end_idx++) {
270 slotptr->name0_4[end_idx * 2] = 0xff;
271 slotptr->name0_4[end_idx * 2 + 1] = 0xff;
272 }
273 end_idx = 5;
274name5_10:
275 end_idx -= 5;
276 for (; end_idx < 6; end_idx++) {
277 slotptr->name5_10[end_idx * 2] = 0xff;
278 slotptr->name5_10[end_idx * 2 + 1] = 0xff;
279 }
280 end_idx = 11;
281name11_12:
282 end_idx -= 11;
283 for (; end_idx < 2; end_idx++) {
284 slotptr->name11_12[end_idx * 2] = 0xff;
285 slotptr->name11_12[end_idx * 2 + 1] = 0xff;
286 }
287
288 return 1;
289}
290
AKASHI Takahiroa2c0bd02019-05-24 14:10:36 +0900291static int new_dir_table(fat_itr *itr);
292static int flush_dir(fat_itr *itr);
Donggeun Kim8f814002011-10-24 21:15:28 +0000293
Heinrich Schuchardtac4ab752020-11-21 08:32:50 +0100294/**
295 * fill_dir_slot() - fill directory entries for long name
296 *
297 * @itr: directory iterator
298 * @l_name: long name
299 * @shortname: short name
300 * Return: 0 for success, -errno otherwise
Donggeun Kim8f814002011-10-24 21:15:28 +0000301 */
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +0900302static int
Heinrich Schuchardtac4ab752020-11-21 08:32:50 +0100303fill_dir_slot(fat_itr *itr, const char *l_name, const char *shortname)
Donggeun Kim8f814002011-10-24 21:15:28 +0000304{
Tien Fong Chee0117dbe2016-07-27 23:08:56 -0700305 __u8 temp_dir_slot_buffer[MAX_LFN_SLOT * sizeof(dir_slot)];
306 dir_slot *slotptr = (dir_slot *)temp_dir_slot_buffer;
Anatolij Gustschinf4f9d5d2011-12-15 03:12:14 +0000307 __u8 counter = 0, checksum;
Donggeun Kim8f814002011-10-24 21:15:28 +0000308 int idx = 0, ret;
Donggeun Kim8f814002011-10-24 21:15:28 +0000309
Stefan Brüns6c617d62016-09-11 22:51:39 +0200310 /* Get short file name checksum value */
Heinrich Schuchardtac4ab752020-11-21 08:32:50 +0100311 checksum = mkcksum(shortname, shortname + 8);
Donggeun Kim8f814002011-10-24 21:15:28 +0000312
313 do {
314 memset(slotptr, 0x00, sizeof(dir_slot));
315 ret = str2slot(slotptr, l_name, &idx);
316 slotptr->id = ++counter;
317 slotptr->attr = ATTR_VFAT;
318 slotptr->alias_checksum = checksum;
319 slotptr++;
320 } while (ret == 0);
321
322 slotptr--;
323 slotptr->id |= LAST_LONG_ENTRY_MASK;
324
325 while (counter >= 1) {
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +0900326 memcpy(itr->dent, slotptr, sizeof(dir_slot));
Donggeun Kim8f814002011-10-24 21:15:28 +0000327 slotptr--;
328 counter--;
AKASHI Takahiroa2c0bd02019-05-24 14:10:36 +0900329
330 if (itr->remaining == 0)
331 flush_dir(itr);
332
AKASHI Takahiroc3269332019-05-24 14:10:37 +0900333 /* allocate a cluster for more entries */
Heinrich Schuchardta8021522020-11-19 12:24:44 +0100334 if (!fat_itr_next(itr) && !itr->dent)
335 if ((itr->is_root && itr->fsdata->fatsize != 32) ||
AKASHI Takahiroc3269332019-05-24 14:10:37 +0900336 new_dir_table(itr))
Heinrich Schuchardtac4ab752020-11-21 08:32:50 +0100337 return -EIO;
Donggeun Kim8f814002011-10-24 21:15:28 +0000338 }
339
Donggeun Kim8f814002011-10-24 21:15:28 +0000340 return 0;
341}
342
Donggeun Kim8f814002011-10-24 21:15:28 +0000343/*
Philipp Skadorov16f553d2016-12-15 15:52:53 -0500344 * Set the entry at index 'entry' in a FAT (12/16/32) table.
Donggeun Kim8f814002011-10-24 21:15:28 +0000345 */
346static int set_fatent_value(fsdata *mydata, __u32 entry, __u32 entry_value)
347{
Philipp Skadorov16f553d2016-12-15 15:52:53 -0500348 __u32 bufnum, offset, off16;
349 __u16 val1, val2;
Donggeun Kim8f814002011-10-24 21:15:28 +0000350
351 switch (mydata->fatsize) {
352 case 32:
353 bufnum = entry / FAT32BUFSIZE;
354 offset = entry - bufnum * FAT32BUFSIZE;
355 break;
356 case 16:
357 bufnum = entry / FAT16BUFSIZE;
358 offset = entry - bufnum * FAT16BUFSIZE;
359 break;
Philipp Skadorov16f553d2016-12-15 15:52:53 -0500360 case 12:
361 bufnum = entry / FAT12BUFSIZE;
362 offset = entry - bufnum * FAT12BUFSIZE;
363 break;
Donggeun Kim8f814002011-10-24 21:15:28 +0000364 default:
365 /* Unsupported FAT size */
366 return -1;
367 }
368
369 /* Read a new block of FAT entries into the cache. */
370 if (bufnum != mydata->fatbufnum) {
371 int getsize = FATBUFBLOCKS;
372 __u8 *bufptr = mydata->fatbuf;
373 __u32 fatlength = mydata->fatlength;
374 __u32 startblock = bufnum * FATBUFBLOCKS;
375
Stefan Brüns58bf86f2016-12-17 00:27:50 +0100376 /* Cap length if fatlength is not a multiple of FATBUFBLOCKS */
377 if (startblock + getsize > fatlength)
378 getsize = fatlength - startblock;
Donggeun Kim8f814002011-10-24 21:15:28 +0000379
Stefan Brüns751b31d2016-09-11 22:51:40 +0200380 if (flush_dirty_fat_buffer(mydata) < 0)
381 return -1;
Donggeun Kim8f814002011-10-24 21:15:28 +0000382
Stefan Brüns58bf86f2016-12-17 00:27:50 +0100383 startblock += mydata->fat_sect;
384
Donggeun Kim8f814002011-10-24 21:15:28 +0000385 if (disk_read(startblock, getsize, bufptr) < 0) {
386 debug("Error reading FAT blocks\n");
387 return -1;
388 }
389 mydata->fatbufnum = bufnum;
390 }
391
Stefan Brüns751b31d2016-09-11 22:51:40 +0200392 /* Mark as dirty */
393 mydata->fat_dirty = 1;
394
Donggeun Kim8f814002011-10-24 21:15:28 +0000395 /* Set the actual entry */
396 switch (mydata->fatsize) {
397 case 32:
398 ((__u32 *) mydata->fatbuf)[offset] = cpu_to_le32(entry_value);
399 break;
400 case 16:
401 ((__u16 *) mydata->fatbuf)[offset] = cpu_to_le16(entry_value);
402 break;
Philipp Skadorov16f553d2016-12-15 15:52:53 -0500403 case 12:
404 off16 = (offset * 3) / 4;
405
406 switch (offset & 0x3) {
407 case 0:
408 val1 = cpu_to_le16(entry_value) & 0xfff;
409 ((__u16 *)mydata->fatbuf)[off16] &= ~0xfff;
410 ((__u16 *)mydata->fatbuf)[off16] |= val1;
411 break;
412 case 1:
413 val1 = cpu_to_le16(entry_value) & 0xf;
414 val2 = (cpu_to_le16(entry_value) >> 4) & 0xff;
415
416 ((__u16 *)mydata->fatbuf)[off16] &= ~0xf000;
417 ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 12);
418
419 ((__u16 *)mydata->fatbuf)[off16 + 1] &= ~0xff;
420 ((__u16 *)mydata->fatbuf)[off16 + 1] |= val2;
421 break;
422 case 2:
423 val1 = cpu_to_le16(entry_value) & 0xff;
424 val2 = (cpu_to_le16(entry_value) >> 8) & 0xf;
425
426 ((__u16 *)mydata->fatbuf)[off16] &= ~0xff00;
427 ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 8);
428
429 ((__u16 *)mydata->fatbuf)[off16 + 1] &= ~0xf;
430 ((__u16 *)mydata->fatbuf)[off16 + 1] |= val2;
431 break;
432 case 3:
433 val1 = cpu_to_le16(entry_value) & 0xfff;
434 ((__u16 *)mydata->fatbuf)[off16] &= ~0xfff0;
435 ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 4);
436 break;
437 default:
438 break;
439 }
440
441 break;
Donggeun Kim8f814002011-10-24 21:15:28 +0000442 default:
443 return -1;
444 }
445
446 return 0;
447}
448
449/*
Philipp Skadorov16f553d2016-12-15 15:52:53 -0500450 * Determine the next free cluster after 'entry' in a FAT (12/16/32) table
Stefan Brüns14999402016-09-11 22:51:41 +0200451 * and link it to 'entry'. EOC marker is not set on returned entry.
Donggeun Kim8f814002011-10-24 21:15:28 +0000452 */
453static __u32 determine_fatent(fsdata *mydata, __u32 entry)
454{
455 __u32 next_fat, next_entry = entry + 1;
456
457 while (1) {
Stefan Brüns9f95d812016-12-17 00:27:51 +0100458 next_fat = get_fatent(mydata, next_entry);
Donggeun Kim8f814002011-10-24 21:15:28 +0000459 if (next_fat == 0) {
Stefan Brüns14999402016-09-11 22:51:41 +0200460 /* found free entry, link to entry */
Donggeun Kim8f814002011-10-24 21:15:28 +0000461 set_fatent_value(mydata, entry, next_entry);
462 break;
463 }
464 next_entry++;
465 }
466 debug("FAT%d: entry: %08x, entry_value: %04x\n",
467 mydata->fatsize, entry, next_entry);
468
469 return next_entry;
470}
471
Heinrich Schuchardt9fb8db42018-10-02 09:30:45 +0200472/**
AKASHI Takahirod98c6742019-05-24 14:10:35 +0900473 * set_sectors() - write data to sectors
Heinrich Schuchardt9fb8db42018-10-02 09:30:45 +0200474 *
AKASHI Takahirod98c6742019-05-24 14:10:35 +0900475 * Write 'size' bytes from 'buffer' into the specified sector.
Heinrich Schuchardt9fb8db42018-10-02 09:30:45 +0200476 *
477 * @mydata: data to be written
AKASHI Takahirod98c6742019-05-24 14:10:35 +0900478 * @startsect: sector to be written to
Heinrich Schuchardt9fb8db42018-10-02 09:30:45 +0200479 * @buffer: data to be written
480 * @size: bytes to be written (but not more than the size of a cluster)
481 * Return: 0 on success, -1 otherwise
Donggeun Kim8f814002011-10-24 21:15:28 +0000482 */
483static int
AKASHI Takahirod98c6742019-05-24 14:10:35 +0900484set_sectors(fsdata *mydata, u32 startsect, u8 *buffer, u32 size)
Donggeun Kim8f814002011-10-24 21:15:28 +0000485{
AKASHI Takahirod98c6742019-05-24 14:10:35 +0900486 u32 nsects = 0;
Benoît Thébaudeauccc945b2015-09-28 15:45:28 +0200487 int ret;
Donggeun Kim8f814002011-10-24 21:15:28 +0000488
AKASHI Takahirod98c6742019-05-24 14:10:35 +0900489 debug("startsect: %d\n", startsect);
Donggeun Kim8f814002011-10-24 21:15:28 +0000490
Benoît Thébaudeauccc945b2015-09-28 15:45:28 +0200491 if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {
492 ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
493
Heinrich Schuchardtaa9d6ba2018-09-13 19:42:47 +0200494 debug("FAT: Misaligned buffer address (%p)\n", buffer);
Benoît Thébaudeauccc945b2015-09-28 15:45:28 +0200495
496 while (size >= mydata->sect_size) {
497 memcpy(tmpbuf, buffer, mydata->sect_size);
498 ret = disk_write(startsect++, 1, tmpbuf);
499 if (ret != 1) {
500 debug("Error writing data (got %d)\n", ret);
501 return -1;
502 }
503
504 buffer += mydata->sect_size;
505 size -= mydata->sect_size;
506 }
507 } else if (size >= mydata->sect_size) {
AKASHI Takahirod98c6742019-05-24 14:10:35 +0900508 nsects = size / mydata->sect_size;
509 ret = disk_write(startsect, nsects, buffer);
510 if (ret != nsects) {
Benoît Thébaudeauccc945b2015-09-28 15:45:28 +0200511 debug("Error writing data (got %d)\n", ret);
Wu, Joshb0e38dd2013-07-24 17:55:30 +0800512 return -1;
513 }
Donggeun Kim8f814002011-10-24 21:15:28 +0000514
AKASHI Takahirod98c6742019-05-24 14:10:35 +0900515 startsect += nsects;
516 buffer += nsects * mydata->sect_size;
517 size -= nsects * mydata->sect_size;
Benoît Thébaudeauccc945b2015-09-28 15:45:28 +0200518 }
Donggeun Kim8f814002011-10-24 21:15:28 +0000519
Benoît Thébaudeauccc945b2015-09-28 15:45:28 +0200520 if (size) {
521 ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
Heinrich Schuchardt9fb8db42018-10-02 09:30:45 +0200522 /* Do not leak content of stack */
523 memset(tmpbuf, 0, mydata->sect_size);
Benoît Thébaudeauccc945b2015-09-28 15:45:28 +0200524 memcpy(tmpbuf, buffer, size);
525 ret = disk_write(startsect, 1, tmpbuf);
526 if (ret != 1) {
527 debug("Error writing data (got %d)\n", ret);
Donggeun Kim8f814002011-10-24 21:15:28 +0000528 return -1;
529 }
Donggeun Kim8f814002011-10-24 21:15:28 +0000530 }
531
532 return 0;
533}
534
AKASHI Takahirod98c6742019-05-24 14:10:35 +0900535/**
536 * set_cluster() - write data to cluster
537 *
538 * Write 'size' bytes from 'buffer' into the specified cluster.
539 *
540 * @mydata: data to be written
541 * @clustnum: cluster to be written to
542 * @buffer: data to be written
543 * @size: bytes to be written (but not more than the size of a cluster)
544 * Return: 0 on success, -1 otherwise
545 */
546static int
547set_cluster(fsdata *mydata, u32 clustnum, u8 *buffer, u32 size)
548{
549 return set_sectors(mydata, clust_to_sect(mydata, clustnum),
550 buffer, size);
551}
552
553static int
554flush_dir(fat_itr *itr)
555{
556 fsdata *mydata = itr->fsdata;
557 u32 startsect, sect_offset, nsects;
558
559 if (!itr->is_root || mydata->fatsize == 32)
560 return set_cluster(mydata, itr->clust, itr->block,
561 mydata->clust_size * mydata->sect_size);
562
563 sect_offset = itr->clust * mydata->clust_size;
564 startsect = mydata->rootdir_sect + sect_offset;
565 /* do not write past the end of rootdir */
566 nsects = min_t(u32, mydata->clust_size,
567 mydata->rootdir_size - sect_offset);
568
569 return set_sectors(mydata, startsect, itr->block,
570 nsects * mydata->sect_size);
571}
572
AKASHI Takahirob07f7042018-09-11 15:59:06 +0900573/*
574 * Read and modify data on existing and consecutive cluster blocks
575 */
576static int
577get_set_cluster(fsdata *mydata, __u32 clustnum, loff_t pos, __u8 *buffer,
578 loff_t size, loff_t *gotsize)
579{
Heinrich Schuchardt8a3ac6e2020-07-06 07:48:14 +0200580 static u8 *tmpbuf_cluster;
AKASHI Takahirob07f7042018-09-11 15:59:06 +0900581 unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
582 __u32 startsect;
583 loff_t wsize;
584 int clustcount, i, ret;
585
586 *gotsize = 0;
587 if (!size)
588 return 0;
589
Heinrich Schuchardt8a3ac6e2020-07-06 07:48:14 +0200590 if (!tmpbuf_cluster) {
591 tmpbuf_cluster = memalign(ARCH_DMA_MINALIGN, MAX_CLUSTSIZE);
592 if (!tmpbuf_cluster)
593 return -1;
594 }
595
AKASHI Takahirob07f7042018-09-11 15:59:06 +0900596 assert(pos < bytesperclust);
597 startsect = clust_to_sect(mydata, clustnum);
598
599 debug("clustnum: %d, startsect: %d, pos: %lld\n",
600 clustnum, startsect, pos);
601
602 /* partial write at beginning */
603 if (pos) {
604 wsize = min(bytesperclust - pos, size);
605 ret = disk_read(startsect, mydata->clust_size, tmpbuf_cluster);
606 if (ret != mydata->clust_size) {
607 debug("Error reading data (got %d)\n", ret);
608 return -1;
609 }
610
611 memcpy(tmpbuf_cluster + pos, buffer, wsize);
612 ret = disk_write(startsect, mydata->clust_size, tmpbuf_cluster);
613 if (ret != mydata->clust_size) {
614 debug("Error writing data (got %d)\n", ret);
615 return -1;
616 }
617
618 size -= wsize;
619 buffer += wsize;
620 *gotsize += wsize;
621
622 startsect += mydata->clust_size;
623
624 if (!size)
625 return 0;
626 }
627
628 /* full-cluster write */
629 if (size >= bytesperclust) {
630 clustcount = lldiv(size, bytesperclust);
631
632 if (!((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1))) {
633 wsize = clustcount * bytesperclust;
634 ret = disk_write(startsect,
635 clustcount * mydata->clust_size,
636 buffer);
637 if (ret != clustcount * mydata->clust_size) {
638 debug("Error writing data (got %d)\n", ret);
639 return -1;
640 }
641
642 size -= wsize;
643 buffer += wsize;
644 *gotsize += wsize;
645
646 startsect += clustcount * mydata->clust_size;
647 } else {
648 for (i = 0; i < clustcount; i++) {
649 memcpy(tmpbuf_cluster, buffer, bytesperclust);
650 ret = disk_write(startsect,
651 mydata->clust_size,
652 tmpbuf_cluster);
653 if (ret != mydata->clust_size) {
654 debug("Error writing data (got %d)\n",
655 ret);
656 return -1;
657 }
658
659 size -= bytesperclust;
660 buffer += bytesperclust;
661 *gotsize += bytesperclust;
662
663 startsect += mydata->clust_size;
664 }
665 }
666 }
667
668 /* partial write at end */
669 if (size) {
670 wsize = size;
671 ret = disk_read(startsect, mydata->clust_size, tmpbuf_cluster);
672 if (ret != mydata->clust_size) {
673 debug("Error reading data (got %d)\n", ret);
674 return -1;
675 }
676 memcpy(tmpbuf_cluster, buffer, wsize);
677 ret = disk_write(startsect, mydata->clust_size, tmpbuf_cluster);
678 if (ret != mydata->clust_size) {
679 debug("Error writing data (got %d)\n", ret);
680 return -1;
681 }
682
683 size -= wsize;
684 buffer += wsize;
685 *gotsize += wsize;
686 }
687
688 assert(!size);
689
690 return 0;
691}
692
Donggeun Kim8f814002011-10-24 21:15:28 +0000693/*
694 * Find the first empty cluster
695 */
696static int find_empty_cluster(fsdata *mydata)
697{
698 __u32 fat_val, entry = 3;
699
700 while (1) {
Stefan Brüns9f95d812016-12-17 00:27:51 +0100701 fat_val = get_fatent(mydata, entry);
Donggeun Kim8f814002011-10-24 21:15:28 +0000702 if (fat_val == 0)
703 break;
704 entry++;
705 }
706
707 return entry;
708}
709
710/*
AKASHI Takahiroa2c0bd02019-05-24 14:10:36 +0900711 * Allocate a cluster for additional directory entries
Donggeun Kim8f814002011-10-24 21:15:28 +0000712 */
AKASHI Takahiroa2c0bd02019-05-24 14:10:36 +0900713static int new_dir_table(fat_itr *itr)
Donggeun Kim8f814002011-10-24 21:15:28 +0000714{
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +0900715 fsdata *mydata = itr->fsdata;
Donggeun Kim8f814002011-10-24 21:15:28 +0000716 int dir_newclust = 0;
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +0900717 unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
Donggeun Kim8f814002011-10-24 21:15:28 +0000718
Donggeun Kim8f814002011-10-24 21:15:28 +0000719 dir_newclust = find_empty_cluster(mydata);
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +0900720 set_fatent_value(mydata, itr->clust, dir_newclust);
Donggeun Kim8f814002011-10-24 21:15:28 +0000721 if (mydata->fatsize == 32)
722 set_fatent_value(mydata, dir_newclust, 0xffffff8);
723 else if (mydata->fatsize == 16)
724 set_fatent_value(mydata, dir_newclust, 0xfff8);
Philipp Skadorov16f553d2016-12-15 15:52:53 -0500725 else if (mydata->fatsize == 12)
726 set_fatent_value(mydata, dir_newclust, 0xff8);
Donggeun Kim8f814002011-10-24 21:15:28 +0000727
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +0900728 itr->clust = dir_newclust;
729 itr->next_clust = dir_newclust;
Donggeun Kim8f814002011-10-24 21:15:28 +0000730
Stefan Brüns751b31d2016-09-11 22:51:40 +0200731 if (flush_dirty_fat_buffer(mydata) < 0)
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +0900732 return -1;
733
734 memset(itr->block, 0x00, bytesperclust);
Donggeun Kim8f814002011-10-24 21:15:28 +0000735
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +0900736 itr->dent = (dir_entry *)itr->block;
737 itr->last_cluster = 1;
738 itr->remaining = bytesperclust / sizeof(dir_entry) - 1;
Donggeun Kim8f814002011-10-24 21:15:28 +0000739
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +0900740 return 0;
Donggeun Kim8f814002011-10-24 21:15:28 +0000741}
742
743/*
744 * Set empty cluster from 'entry' to the end of a file
745 */
746static int clear_fatent(fsdata *mydata, __u32 entry)
747{
748 __u32 fat_val;
749
Philipp Skadorov16f553d2016-12-15 15:52:53 -0500750 while (!CHECK_CLUST(entry, mydata->fatsize)) {
Stefan Brüns9f95d812016-12-17 00:27:51 +0100751 fat_val = get_fatent(mydata, entry);
Donggeun Kim8f814002011-10-24 21:15:28 +0000752 if (fat_val != 0)
753 set_fatent_value(mydata, entry, 0);
754 else
755 break;
756
Donggeun Kim8f814002011-10-24 21:15:28 +0000757 entry = fat_val;
758 }
759
760 /* Flush fat buffer */
Stefan Brüns751b31d2016-09-11 22:51:40 +0200761 if (flush_dirty_fat_buffer(mydata) < 0)
Donggeun Kim8f814002011-10-24 21:15:28 +0000762 return -1;
763
764 return 0;
765}
766
767/*
AKASHI Takahiroed8b1e42018-09-11 15:59:05 +0900768 * Set start cluster in directory entry
769 */
770static void set_start_cluster(const fsdata *mydata, dir_entry *dentptr,
771 __u32 start_cluster)
772{
773 if (mydata->fatsize == 32)
774 dentptr->starthi =
775 cpu_to_le16((start_cluster & 0xffff0000) >> 16);
776 dentptr->start = cpu_to_le16(start_cluster & 0xffff);
777}
778
779/*
780 * Check whether adding a file makes the file system to
781 * exceed the size of the block device
782 * Return -1 when overflow occurs, otherwise return 0
783 */
784static int check_overflow(fsdata *mydata, __u32 clustnum, loff_t size)
785{
786 __u32 startsect, sect_num, offset;
787
788 if (clustnum > 0)
789 startsect = clust_to_sect(mydata, clustnum);
790 else
791 startsect = mydata->rootdir_sect;
792
793 sect_num = div_u64_rem(size, mydata->sect_size, &offset);
794
795 if (offset != 0)
796 sect_num++;
797
798 if (startsect + sect_num > total_sector)
799 return -1;
800 return 0;
801}
802
803/*
Donggeun Kim8f814002011-10-24 21:15:28 +0000804 * Write at most 'maxsize' bytes from 'buffer' into
805 * the file associated with 'dentptr'
Suriyan Ramasami441c2232014-11-17 14:39:35 -0800806 * Update the number of bytes written in *gotsize and return 0
807 * or return -1 on fatal errors.
Donggeun Kim8f814002011-10-24 21:15:28 +0000808 */
809static int
AKASHI Takahiroed8b1e42018-09-11 15:59:05 +0900810set_contents(fsdata *mydata, dir_entry *dentptr, loff_t pos, __u8 *buffer,
811 loff_t maxsize, loff_t *gotsize)
Donggeun Kim8f814002011-10-24 21:15:28 +0000812{
Donggeun Kim8f814002011-10-24 21:15:28 +0000813 unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
814 __u32 curclust = START(dentptr);
815 __u32 endclust = 0, newclust = 0;
Heinrich Schuchardtdb538a92019-02-25 19:42:48 +0100816 u64 cur_pos, filesize;
817 loff_t offset, actsize, wsize;
Donggeun Kim8f814002011-10-24 21:15:28 +0000818
Suriyan Ramasami441c2232014-11-17 14:39:35 -0800819 *gotsize = 0;
AKASHI Takahirob07f7042018-09-11 15:59:06 +0900820 filesize = pos + maxsize;
Donggeun Kim8f814002011-10-24 21:15:28 +0000821
Suriyan Ramasami441c2232014-11-17 14:39:35 -0800822 debug("%llu bytes\n", filesize);
Donggeun Kim8f814002011-10-24 21:15:28 +0000823
AKASHI Takahirob07f7042018-09-11 15:59:06 +0900824 if (!filesize) {
825 if (!curclust)
826 return 0;
827 if (!CHECK_CLUST(curclust, mydata->fatsize) ||
828 IS_LAST_CLUST(curclust, mydata->fatsize)) {
829 clear_fatent(mydata, curclust);
830 set_start_cluster(mydata, dentptr, 0);
831 return 0;
832 }
833 debug("curclust: 0x%x\n", curclust);
834 debug("Invalid FAT entry\n");
835 return -1;
836 }
837
838 if (!curclust) {
839 assert(pos == 0);
840 goto set_clusters;
841 }
842
843 /* go to cluster at pos */
844 cur_pos = bytesperclust;
845 while (1) {
846 if (pos <= cur_pos)
847 break;
848 if (IS_LAST_CLUST(curclust, mydata->fatsize))
849 break;
850
851 newclust = get_fatent(mydata, curclust);
852 if (!IS_LAST_CLUST(newclust, mydata->fatsize) &&
853 CHECK_CLUST(newclust, mydata->fatsize)) {
854 debug("curclust: 0x%x\n", curclust);
855 debug("Invalid FAT entry\n");
Benoît Thébaudeau8fed7d32015-09-28 15:45:32 +0200856 return -1;
857 }
AKASHI Takahirob07f7042018-09-11 15:59:06 +0900858
859 cur_pos += bytesperclust;
860 curclust = newclust;
861 }
862 if (IS_LAST_CLUST(curclust, mydata->fatsize)) {
863 assert(pos == cur_pos);
864 goto set_clusters;
865 }
866
867 assert(pos < cur_pos);
868 cur_pos -= bytesperclust;
869
870 /* overwrite */
871 assert(IS_LAST_CLUST(curclust, mydata->fatsize) ||
872 !CHECK_CLUST(curclust, mydata->fatsize));
873
874 while (1) {
875 /* search for allocated consecutive clusters */
876 actsize = bytesperclust;
877 endclust = curclust;
878 while (1) {
879 if (filesize <= (cur_pos + actsize))
880 break;
881
882 newclust = get_fatent(mydata, endclust);
883
Marek Szyprowski2f241672019-12-02 12:11:13 +0100884 if (newclust != endclust + 1)
885 break;
AKASHI Takahirob07f7042018-09-11 15:59:06 +0900886 if (IS_LAST_CLUST(newclust, mydata->fatsize))
887 break;
888 if (CHECK_CLUST(newclust, mydata->fatsize)) {
889 debug("curclust: 0x%x\n", curclust);
890 debug("Invalid FAT entry\n");
891 return -1;
892 }
893
894 actsize += bytesperclust;
895 endclust = newclust;
896 }
897
898 /* overwrite to <curclust..endclust> */
899 if (pos < cur_pos)
900 offset = 0;
901 else
902 offset = pos - cur_pos;
Marek Szyprowski3a867ce2019-12-02 12:11:14 +0100903 wsize = min_t(unsigned long long, actsize, filesize - cur_pos);
904 wsize -= offset;
905
AKASHI Takahirob07f7042018-09-11 15:59:06 +0900906 if (get_set_cluster(mydata, curclust, offset,
907 buffer, wsize, &actsize)) {
908 printf("Error get-and-setting cluster\n");
909 return -1;
910 }
911 buffer += wsize;
912 *gotsize += wsize;
913 cur_pos += offset + wsize;
914
915 if (filesize <= cur_pos)
916 break;
917
AKASHI Takahirob07f7042018-09-11 15:59:06 +0900918 if (IS_LAST_CLUST(newclust, mydata->fatsize))
919 /* no more clusters */
920 break;
921
922 curclust = newclust;
923 }
924
925 if (filesize <= cur_pos) {
926 /* no more write */
927 newclust = get_fatent(mydata, endclust);
928 if (!IS_LAST_CLUST(newclust, mydata->fatsize)) {
929 /* truncate the rest */
930 clear_fatent(mydata, newclust);
931
932 /* Mark end of file in FAT */
933 if (mydata->fatsize == 12)
934 newclust = 0xfff;
935 else if (mydata->fatsize == 16)
936 newclust = 0xffff;
937 else if (mydata->fatsize == 32)
938 newclust = 0xfffffff;
939 set_fatent_value(mydata, endclust, newclust);
940 }
941
942 return 0;
AKASHI Takahiroed8b1e42018-09-11 15:59:05 +0900943 }
944
AKASHI Takahirob07f7042018-09-11 15:59:06 +0900945 curclust = endclust;
946 filesize -= cur_pos;
Heinrich Schuchardtdb538a92019-02-25 19:42:48 +0100947 assert(!do_div(cur_pos, bytesperclust));
AKASHI Takahiroed8b1e42018-09-11 15:59:05 +0900948
AKASHI Takahirob07f7042018-09-11 15:59:06 +0900949set_clusters:
950 /* allocate and write */
951 assert(!pos);
952
953 /* Assure that curclust is valid */
954 if (!curclust) {
955 curclust = find_empty_cluster(mydata);
956 set_start_cluster(mydata, dentptr, curclust);
957 } else {
958 newclust = get_fatent(mydata, curclust);
959
960 if (IS_LAST_CLUST(newclust, mydata->fatsize)) {
961 newclust = determine_fatent(mydata, curclust);
962 set_fatent_value(mydata, curclust, newclust);
963 curclust = newclust;
964 } else {
965 debug("error: something wrong\n");
966 return -1;
967 }
968 }
969
970 /* TODO: already partially written */
AKASHI Takahiroed8b1e42018-09-11 15:59:05 +0900971 if (check_overflow(mydata, curclust, filesize)) {
972 printf("Error: no space left: %llu\n", filesize);
973 return -1;
Benoît Thébaudeau8fed7d32015-09-28 15:45:32 +0200974 }
975
Donggeun Kim8f814002011-10-24 21:15:28 +0000976 actsize = bytesperclust;
977 endclust = curclust;
978 do {
979 /* search for consecutive clusters */
980 while (actsize < filesize) {
981 newclust = determine_fatent(mydata, endclust);
982
983 if ((newclust - 1) != endclust)
AKASHI Takahiroed8b1e42018-09-11 15:59:05 +0900984 /* write to <curclust..endclust> */
Donggeun Kim8f814002011-10-24 21:15:28 +0000985 goto getit;
986
987 if (CHECK_CLUST(newclust, mydata->fatsize)) {
Benoît Thébaudeaue0b86942015-09-28 15:45:30 +0200988 debug("newclust: 0x%x\n", newclust);
Donggeun Kim8f814002011-10-24 21:15:28 +0000989 debug("Invalid FAT entry\n");
Suriyan Ramasami441c2232014-11-17 14:39:35 -0800990 return 0;
Donggeun Kim8f814002011-10-24 21:15:28 +0000991 }
992 endclust = newclust;
993 actsize += bytesperclust;
994 }
Donggeun Kim8f814002011-10-24 21:15:28 +0000995
996 /* set remaining bytes */
Donggeun Kim8f814002011-10-24 21:15:28 +0000997 actsize = filesize;
Heinrich Schuchardt9fb8db42018-10-02 09:30:45 +0200998 if (set_cluster(mydata, curclust, buffer, (u32)actsize) != 0) {
Donggeun Kim8f814002011-10-24 21:15:28 +0000999 debug("error: writing cluster\n");
1000 return -1;
1001 }
Suriyan Ramasami441c2232014-11-17 14:39:35 -08001002 *gotsize += actsize;
Donggeun Kim8f814002011-10-24 21:15:28 +00001003
1004 /* Mark end of file in FAT */
Philipp Skadorov16f553d2016-12-15 15:52:53 -05001005 if (mydata->fatsize == 12)
1006 newclust = 0xfff;
1007 else if (mydata->fatsize == 16)
Donggeun Kim8f814002011-10-24 21:15:28 +00001008 newclust = 0xffff;
1009 else if (mydata->fatsize == 32)
1010 newclust = 0xfffffff;
1011 set_fatent_value(mydata, endclust, newclust);
1012
Suriyan Ramasami441c2232014-11-17 14:39:35 -08001013 return 0;
Donggeun Kim8f814002011-10-24 21:15:28 +00001014getit:
Heinrich Schuchardt9fb8db42018-10-02 09:30:45 +02001015 if (set_cluster(mydata, curclust, buffer, (u32)actsize) != 0) {
Donggeun Kim8f814002011-10-24 21:15:28 +00001016 debug("error: writing cluster\n");
1017 return -1;
1018 }
Suriyan Ramasami441c2232014-11-17 14:39:35 -08001019 *gotsize += actsize;
Donggeun Kim8f814002011-10-24 21:15:28 +00001020 filesize -= actsize;
1021 buffer += actsize;
1022
Benoît Thébaudeaue0b86942015-09-28 15:45:30 +02001023 if (CHECK_CLUST(newclust, mydata->fatsize)) {
1024 debug("newclust: 0x%x\n", newclust);
Donggeun Kim8f814002011-10-24 21:15:28 +00001025 debug("Invalid FAT entry\n");
Suriyan Ramasami441c2232014-11-17 14:39:35 -08001026 return 0;
Donggeun Kim8f814002011-10-24 21:15:28 +00001027 }
1028 actsize = bytesperclust;
1029 curclust = endclust = newclust;
1030 } while (1);
Donggeun Kim8f814002011-10-24 21:15:28 +00001031
AKASHI Takahiroed8b1e42018-09-11 15:59:05 +09001032 return 0;
Benoît Thébaudeau8fed7d32015-09-28 15:45:32 +02001033}
1034
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +01001035/**
1036 * fill_dentry() - fill directory entry with shortname
1037 *
1038 * @mydata: private filesystem parameters
1039 * @dentptr: directory entry
1040 * @shortname: chosen short name
1041 * @start_cluster: first cluster of file
1042 * @size: file size
1043 * @attr: file attributes
Benoît Thébaudeau8fed7d32015-09-28 15:45:32 +02001044 */
1045static void fill_dentry(fsdata *mydata, dir_entry *dentptr,
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +01001046 const char *shortname, __u32 start_cluster, __u32 size, __u8 attr)
Benoît Thébaudeau8fed7d32015-09-28 15:45:32 +02001047{
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +01001048 memset(dentptr, 0, sizeof(*dentptr));
1049
Benoît Thébaudeau8fed7d32015-09-28 15:45:32 +02001050 set_start_cluster(mydata, dentptr, start_cluster);
Donggeun Kim8f814002011-10-24 21:15:28 +00001051 dentptr->size = cpu_to_le32(size);
1052
1053 dentptr->attr = attr;
1054
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +01001055 memcpy(dentptr->name, shortname, SHORT_NAME_SIZE);
Donggeun Kim8f814002011-10-24 21:15:28 +00001056}
1057
1058/*
Donggeun Kim8f814002011-10-24 21:15:28 +00001059 * Find a directory entry based on filename or start cluster number
1060 * If the directory entry is not found,
1061 * the new position for writing a directory entry will be returned
1062 */
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001063static dir_entry *find_directory_entry(fat_itr *itr, char *filename)
Donggeun Kim8f814002011-10-24 21:15:28 +00001064{
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001065 int match = 0;
Donggeun Kim8f814002011-10-24 21:15:28 +00001066
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001067 while (fat_itr_next(itr)) {
1068 /* check both long and short name: */
1069 if (!strcasecmp(filename, itr->name))
1070 match = 1;
1071 else if (itr->name != itr->s_name &&
1072 !strcasecmp(filename, itr->s_name))
1073 match = 1;
Donggeun Kim8f814002011-10-24 21:15:28 +00001074
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001075 if (!match)
1076 continue;
Donggeun Kim8f814002011-10-24 21:15:28 +00001077
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001078 if (itr->dent->name[0] == '\0')
Donggeun Kim8f814002011-10-24 21:15:28 +00001079 return NULL;
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001080 else
1081 return itr->dent;
1082 }
Donggeun Kim8f814002011-10-24 21:15:28 +00001083
AKASHI Takahiroc3269332019-05-24 14:10:37 +09001084 /* allocate a cluster for more entries */
1085 if (!itr->dent &&
1086 (!itr->is_root || itr->fsdata->fatsize == 32) &&
1087 new_dir_table(itr))
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001088 /* indicate that allocating dent failed */
1089 itr->dent = NULL;
Donggeun Kim8f814002011-10-24 21:15:28 +00001090
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001091 return NULL;
1092}
Donggeun Kim8f814002011-10-24 21:15:28 +00001093
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001094static int split_filename(char *filename, char **dirname, char **basename)
1095{
1096 char *p, *last_slash, *last_slash_cont;
Donggeun Kim8f814002011-10-24 21:15:28 +00001097
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001098again:
1099 p = filename;
1100 last_slash = NULL;
1101 last_slash_cont = NULL;
1102 while (*p) {
1103 if (ISDIRDELIM(*p)) {
1104 last_slash = p;
1105 last_slash_cont = p;
1106 /* continuous slashes */
1107 while (ISDIRDELIM(*p))
1108 last_slash_cont = p++;
1109 if (!*p)
1110 break;
Donggeun Kim8f814002011-10-24 21:15:28 +00001111 }
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001112 p++;
1113 }
Wu, Josh675b23c2014-05-08 16:14:07 +08001114
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001115 if (last_slash) {
1116 if (last_slash_cont == (filename + strlen(filename) - 1)) {
1117 /* remove trailing slashes */
1118 *last_slash = '\0';
1119 goto again;
Wu, Josh675b23c2014-05-08 16:14:07 +08001120 }
1121
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001122 if (last_slash == filename) {
1123 /* avoid ""(null) directory */
1124 *dirname = "/";
1125 } else {
1126 *last_slash = '\0';
1127 *dirname = filename;
Donggeun Kim8f814002011-10-24 21:15:28 +00001128 }
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001129
1130 *last_slash_cont = '\0';
1131 *basename = last_slash_cont + 1;
1132 } else {
1133 *dirname = "/"; /* root by default */
1134 *basename = filename;
Donggeun Kim8f814002011-10-24 21:15:28 +00001135 }
1136
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001137 return 0;
Donggeun Kim8f814002011-10-24 21:15:28 +00001138}
1139
Heinrich Schuchardt58587212019-05-12 09:59:18 +02001140/**
1141 * normalize_longname() - check long file name and convert to lower case
1142 *
1143 * We assume here that the FAT file system is using an 8bit code page.
1144 * Linux typically uses CP437, EDK2 assumes CP1250.
1145 *
1146 * @l_filename: preallocated buffer receiving the normalized name
1147 * @filename: filename to normalize
1148 * Return: 0 on success, -1 on failure
1149 */
AKASHI Takahiroc83df1a2018-09-11 15:59:02 +09001150static int normalize_longname(char *l_filename, const char *filename)
1151{
Heinrich Schuchardt58587212019-05-12 09:59:18 +02001152 const char *p, illegal[] = "<>:\"/\\|?*";
AKASHI Takahiroc83df1a2018-09-11 15:59:02 +09001153
Heinrich Schuchardt58587212019-05-12 09:59:18 +02001154 if (strlen(filename) >= VFAT_MAXLEN_BYTES)
AKASHI Takahiroc83df1a2018-09-11 15:59:02 +09001155 return -1;
AKASHI Takahiroc83df1a2018-09-11 15:59:02 +09001156
Heinrich Schuchardt58587212019-05-12 09:59:18 +02001157 for (p = filename; *p; ++p) {
1158 if ((unsigned char)*p < 0x20)
1159 return -1;
1160 if (strchr(illegal, *p))
1161 return -1;
1162 }
AKASHI Takahiroc83df1a2018-09-11 15:59:02 +09001163
Heinrich Schuchardt58587212019-05-12 09:59:18 +02001164 strcpy(l_filename, filename);
1165 downcase(l_filename, VFAT_MAXLEN_BYTES);
AKASHI Takahiroc83df1a2018-09-11 15:59:02 +09001166
1167 return 0;
1168}
1169
AKASHI Takahiroed8b1e42018-09-11 15:59:05 +09001170int file_fat_write_at(const char *filename, loff_t pos, void *buffer,
1171 loff_t size, loff_t *actwrite)
Donggeun Kim8f814002011-10-24 21:15:28 +00001172{
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001173 dir_entry *retdent;
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001174 fsdata datablock = { .fatbuf = NULL, };
Donggeun Kim8f814002011-10-24 21:15:28 +00001175 fsdata *mydata = &datablock;
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001176 fat_itr *itr = NULL;
AKASHI Takahiroc83df1a2018-09-11 15:59:02 +09001177 int ret = -1;
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001178 char *filename_copy, *parent, *basename;
Donggeun Kim8f814002011-10-24 21:15:28 +00001179 char l_filename[VFAT_MAXLEN_BYTES];
1180
AKASHI Takahiroed8b1e42018-09-11 15:59:05 +09001181 debug("writing %s\n", filename);
1182
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001183 filename_copy = strdup(filename);
1184 if (!filename_copy)
1185 return -ENOMEM;
Donggeun Kim8f814002011-10-24 21:15:28 +00001186
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001187 split_filename(filename_copy, &parent, &basename);
1188 if (!strlen(basename)) {
1189 ret = -EINVAL;
1190 goto exit;
Donggeun Kim8f814002011-10-24 21:15:28 +00001191 }
1192
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001193 filename = basename;
1194 if (normalize_longname(l_filename, filename)) {
1195 printf("FAT: illegal filename (%s)\n", filename);
1196 ret = -EINVAL;
1197 goto exit;
Donggeun Kim8f814002011-10-24 21:15:28 +00001198 }
1199
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001200 itr = malloc_cache_aligned(sizeof(fat_itr));
1201 if (!itr) {
1202 ret = -ENOMEM;
1203 goto exit;
Donggeun Kim8f814002011-10-24 21:15:28 +00001204 }
1205
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001206 ret = fat_itr_root(itr, &datablock);
1207 if (ret)
Donggeun Kim8f814002011-10-24 21:15:28 +00001208 goto exit;
Donggeun Kim8f814002011-10-24 21:15:28 +00001209
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001210 total_sector = datablock.total_sect;
1211
1212 ret = fat_itr_resolve(itr, parent, TYPE_DIR);
1213 if (ret) {
1214 printf("%s: doesn't exist (%d)\n", parent, ret);
AKASHI Takahiroc83df1a2018-09-11 15:59:02 +09001215 goto exit;
1216 }
Donggeun Kim8f814002011-10-24 21:15:28 +00001217
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001218 retdent = find_directory_entry(itr, l_filename);
1219
Donggeun Kim8f814002011-10-24 21:15:28 +00001220 if (retdent) {
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001221 if (fat_itr_isdir(itr)) {
1222 ret = -EISDIR;
1223 goto exit;
1224 }
1225
AKASHI Takahirob07f7042018-09-11 15:59:06 +09001226 /* A file exists */
1227 if (pos == -1)
1228 /* Append to the end */
1229 pos = FAT2CPU32(retdent->size);
1230 if (pos > retdent->size) {
1231 /* No hole allowed */
1232 ret = -EINVAL;
1233 goto exit;
1234 }
1235
AKASHI Takahiroed8b1e42018-09-11 15:59:05 +09001236 /* Update file size in a directory entry */
1237 retdent->size = cpu_to_le32(pos + size);
Donggeun Kim8f814002011-10-24 21:15:28 +00001238 } else {
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001239 /* Create a new file */
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +01001240 char shortname[SHORT_NAME_SIZE];
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001241
1242 if (itr->is_root) {
1243 /* root dir cannot have "." or ".." */
1244 if (!strcmp(l_filename, ".") ||
1245 !strcmp(l_filename, "..")) {
1246 ret = -EINVAL;
1247 goto exit;
1248 }
1249 }
1250
1251 if (!itr->dent) {
1252 printf("Error: allocating new dir entry\n");
1253 ret = -EIO;
1254 goto exit;
1255 }
1256
AKASHI Takahirob07f7042018-09-11 15:59:06 +09001257 if (pos) {
1258 /* No hole allowed */
1259 ret = -EINVAL;
1260 goto exit;
1261 }
1262
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +01001263 /* Check if long name is needed */
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +01001264 ret = set_name(filename, shortname);
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +01001265 if (ret < 0)
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001266 goto exit;
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +01001267 if (ret > 1) {
1268 /* Set long name entries */
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +01001269 ret = fill_dir_slot(itr, filename, shortname);
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +01001270 if (ret)
1271 goto exit;
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001272 }
Donggeun Kim8f814002011-10-24 21:15:28 +00001273
AKASHI Takahiroa2c0bd02019-05-24 14:10:36 +09001274 /* Set short name entry */
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +01001275 fill_dentry(itr->fsdata, itr->dent, shortname, 0, size,
Heinrich Schuchardt881c74a2020-11-22 11:13:33 +01001276 ATTR_ARCH);
Donggeun Kim8f814002011-10-24 21:15:28 +00001277
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001278 retdent = itr->dent;
Benoît Thébaudeaud1d390a2015-09-28 15:45:31 +02001279 }
Donggeun Kim8f814002011-10-24 21:15:28 +00001280
AKASHI Takahiroed8b1e42018-09-11 15:59:05 +09001281 ret = set_contents(mydata, retdent, pos, buffer, size, actwrite);
Benoît Thébaudeaud1d390a2015-09-28 15:45:31 +02001282 if (ret < 0) {
1283 printf("Error: writing contents\n");
AKASHI Takahiro6aa77692018-09-11 15:59:03 +09001284 ret = -EIO;
Benoît Thébaudeaud1d390a2015-09-28 15:45:31 +02001285 goto exit;
1286 }
1287 debug("attempt to write 0x%llx bytes\n", *actwrite);
Donggeun Kim8f814002011-10-24 21:15:28 +00001288
Benoît Thébaudeaud1d390a2015-09-28 15:45:31 +02001289 /* Flush fat buffer */
Stefan Brüns751b31d2016-09-11 22:51:40 +02001290 ret = flush_dirty_fat_buffer(mydata);
Benoît Thébaudeaud1d390a2015-09-28 15:45:31 +02001291 if (ret) {
1292 printf("Error: flush fat buffer\n");
AKASHI Takahiro6aa77692018-09-11 15:59:03 +09001293 ret = -EIO;
Benoît Thébaudeaud1d390a2015-09-28 15:45:31 +02001294 goto exit;
Donggeun Kim8f814002011-10-24 21:15:28 +00001295 }
1296
Benoît Thébaudeaud1d390a2015-09-28 15:45:31 +02001297 /* Write directory table to device */
AKASHI Takahirod98c6742019-05-24 14:10:35 +09001298 ret = flush_dir(itr);
AKASHI Takahiro6aa77692018-09-11 15:59:03 +09001299 if (ret) {
Benoît Thébaudeaud1d390a2015-09-28 15:45:31 +02001300 printf("Error: writing directory entry\n");
AKASHI Takahiro6aa77692018-09-11 15:59:03 +09001301 ret = -EIO;
1302 }
Benoît Thébaudeaud1d390a2015-09-28 15:45:31 +02001303
Donggeun Kim8f814002011-10-24 21:15:28 +00001304exit:
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001305 free(filename_copy);
Donggeun Kim8f814002011-10-24 21:15:28 +00001306 free(mydata->fatbuf);
AKASHI Takahiro49ca96b2018-09-11 15:59:04 +09001307 free(itr);
Suriyan Ramasami441c2232014-11-17 14:39:35 -08001308 return ret;
Donggeun Kim8f814002011-10-24 21:15:28 +00001309}
1310
Suriyan Ramasami441c2232014-11-17 14:39:35 -08001311int file_fat_write(const char *filename, void *buffer, loff_t offset,
1312 loff_t maxsize, loff_t *actwrite)
Donggeun Kim8f814002011-10-24 21:15:28 +00001313{
AKASHI Takahiroed8b1e42018-09-11 15:59:05 +09001314 return file_fat_write_at(filename, offset, buffer, maxsize, actwrite);
Donggeun Kim8f814002011-10-24 21:15:28 +00001315}
AKASHI Takahiro1c24b7b2018-09-11 15:59:10 +09001316
AKASHI Takahiro73b34972018-09-11 15:59:14 +09001317static int fat_dir_entries(fat_itr *itr)
1318{
1319 fat_itr *dirs;
1320 fsdata fsdata = { .fatbuf = NULL, }, *mydata = &fsdata;
1321 /* for FATBUFSIZE */
1322 int count;
1323
1324 dirs = malloc_cache_aligned(sizeof(fat_itr));
1325 if (!dirs) {
1326 debug("Error: allocating memory\n");
1327 count = -ENOMEM;
1328 goto exit;
1329 }
1330
1331 /* duplicate fsdata */
1332 fat_itr_child(dirs, itr);
1333 fsdata = *dirs->fsdata;
1334
1335 /* allocate local fat buffer */
1336 fsdata.fatbuf = malloc_cache_aligned(FATBUFSIZE);
1337 if (!fsdata.fatbuf) {
1338 debug("Error: allocating memory\n");
1339 count = -ENOMEM;
1340 goto exit;
1341 }
1342 fsdata.fatbufnum = -1;
1343 dirs->fsdata = &fsdata;
1344
1345 for (count = 0; fat_itr_next(dirs); count++)
1346 ;
1347
1348exit:
1349 free(fsdata.fatbuf);
1350 free(dirs);
1351 return count;
1352}
1353
1354static int delete_dentry(fat_itr *itr)
1355{
1356 fsdata *mydata = itr->fsdata;
1357 dir_entry *dentptr = itr->dent;
1358
1359 /* free cluster blocks */
1360 clear_fatent(mydata, START(dentptr));
1361 if (flush_dirty_fat_buffer(mydata) < 0) {
1362 printf("Error: flush fat buffer\n");
1363 return -EIO;
1364 }
1365
1366 /*
1367 * update a directory entry
1368 * TODO:
1369 * - long file name support
1370 * - find and mark the "new" first invalid entry as name[0]=0x00
1371 */
1372 memset(dentptr, 0, sizeof(*dentptr));
1373 dentptr->name[0] = 0xe5;
1374
AKASHI Takahirod98c6742019-05-24 14:10:35 +09001375 if (flush_dir(itr)) {
AKASHI Takahiro73b34972018-09-11 15:59:14 +09001376 printf("error: writing directory entry\n");
1377 return -EIO;
1378 }
1379
1380 return 0;
1381}
1382
1383int fat_unlink(const char *filename)
1384{
1385 fsdata fsdata = { .fatbuf = NULL, };
1386 fat_itr *itr = NULL;
1387 int n_entries, ret;
1388 char *filename_copy, *dirname, *basename;
1389
1390 filename_copy = strdup(filename);
Heinrich Schuchardtb2242f62018-10-02 06:58:00 +02001391 if (!filename_copy) {
1392 printf("Error: allocating memory\n");
1393 ret = -ENOMEM;
1394 goto exit;
1395 }
AKASHI Takahiro73b34972018-09-11 15:59:14 +09001396 split_filename(filename_copy, &dirname, &basename);
1397
1398 if (!strcmp(dirname, "/") && !strcmp(basename, "")) {
1399 printf("Error: cannot remove root\n");
1400 ret = -EINVAL;
1401 goto exit;
1402 }
1403
1404 itr = malloc_cache_aligned(sizeof(fat_itr));
1405 if (!itr) {
1406 printf("Error: allocating memory\n");
Heinrich Schuchardtb2242f62018-10-02 06:58:00 +02001407 ret = -ENOMEM;
1408 goto exit;
AKASHI Takahiro73b34972018-09-11 15:59:14 +09001409 }
1410
1411 ret = fat_itr_root(itr, &fsdata);
1412 if (ret)
1413 goto exit;
1414
1415 total_sector = fsdata.total_sect;
1416
1417 ret = fat_itr_resolve(itr, dirname, TYPE_DIR);
1418 if (ret) {
1419 printf("%s: doesn't exist (%d)\n", dirname, ret);
1420 ret = -ENOENT;
1421 goto exit;
1422 }
1423
1424 if (!find_directory_entry(itr, basename)) {
1425 printf("%s: doesn't exist\n", basename);
1426 ret = -ENOENT;
1427 goto exit;
1428 }
1429
1430 if (fat_itr_isdir(itr)) {
1431 n_entries = fat_dir_entries(itr);
1432 if (n_entries < 0) {
1433 ret = n_entries;
1434 goto exit;
1435 }
1436 if (n_entries > 2) {
1437 printf("Error: directory is not empty: %d\n",
1438 n_entries);
1439 ret = -EINVAL;
1440 goto exit;
1441 }
1442 }
1443
1444 ret = delete_dentry(itr);
1445
1446exit:
1447 free(fsdata.fatbuf);
1448 free(itr);
1449 free(filename_copy);
1450
1451 return ret;
1452}
1453
AKASHI Takahiro1c24b7b2018-09-11 15:59:10 +09001454int fat_mkdir(const char *new_dirname)
1455{
1456 dir_entry *retdent;
1457 fsdata datablock = { .fatbuf = NULL, };
1458 fsdata *mydata = &datablock;
1459 fat_itr *itr = NULL;
1460 char *dirname_copy, *parent, *dirname;
1461 char l_dirname[VFAT_MAXLEN_BYTES];
1462 int ret = -1;
1463 loff_t actwrite;
1464 unsigned int bytesperclust;
1465 dir_entry *dotdent = NULL;
1466
1467 dirname_copy = strdup(new_dirname);
1468 if (!dirname_copy)
1469 goto exit;
1470
1471 split_filename(dirname_copy, &parent, &dirname);
1472 if (!strlen(dirname)) {
1473 ret = -EINVAL;
1474 goto exit;
1475 }
1476
1477 if (normalize_longname(l_dirname, dirname)) {
1478 printf("FAT: illegal filename (%s)\n", dirname);
1479 ret = -EINVAL;
1480 goto exit;
1481 }
1482
1483 itr = malloc_cache_aligned(sizeof(fat_itr));
1484 if (!itr) {
1485 ret = -ENOMEM;
1486 goto exit;
1487 }
1488
1489 ret = fat_itr_root(itr, &datablock);
1490 if (ret)
1491 goto exit;
1492
1493 total_sector = datablock.total_sect;
1494
1495 ret = fat_itr_resolve(itr, parent, TYPE_DIR);
1496 if (ret) {
1497 printf("%s: doesn't exist (%d)\n", parent, ret);
1498 goto exit;
1499 }
1500
1501 retdent = find_directory_entry(itr, l_dirname);
1502
1503 if (retdent) {
1504 printf("%s: already exists\n", l_dirname);
1505 ret = -EEXIST;
1506 goto exit;
1507 } else {
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +01001508 char shortname[SHORT_NAME_SIZE];
1509
AKASHI Takahiro1c24b7b2018-09-11 15:59:10 +09001510 if (itr->is_root) {
1511 /* root dir cannot have "." or ".." */
1512 if (!strcmp(l_dirname, ".") ||
1513 !strcmp(l_dirname, "..")) {
1514 ret = -EINVAL;
1515 goto exit;
1516 }
1517 }
1518
1519 if (!itr->dent) {
1520 printf("Error: allocating new dir entry\n");
1521 ret = -EIO;
1522 goto exit;
1523 }
1524
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +01001525 /* Check if long name is needed */
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +01001526 ret = set_name(dirname, shortname);
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +01001527 if (ret < 0)
1528 goto exit;
1529 if (ret > 1) {
1530 /* Set long name entries */
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +01001531 ret = fill_dir_slot(itr, dirname, shortname);
Heinrich Schuchardtba9c44e2020-11-20 12:55:22 +01001532 if (ret)
1533 goto exit;
1534 }
AKASHI Takahiro1c24b7b2018-09-11 15:59:10 +09001535
1536 /* Set attribute as archive for regular file */
Heinrich Schuchardt7afbd602020-11-22 19:19:39 +01001537 fill_dentry(itr->fsdata, itr->dent, shortname, 0, 0,
AKASHI Takahiro1c24b7b2018-09-11 15:59:10 +09001538 ATTR_DIR | ATTR_ARCH);
1539
1540 retdent = itr->dent;
1541 }
1542
1543 /* Default entries */
1544 bytesperclust = mydata->clust_size * mydata->sect_size;
1545 dotdent = malloc_cache_aligned(bytesperclust);
1546 if (!dotdent) {
1547 ret = -ENOMEM;
1548 goto exit;
1549 }
1550 memset(dotdent, 0, bytesperclust);
1551
1552 memcpy(dotdent[0].name, ". ", 8);
1553 memcpy(dotdent[0].ext, " ", 3);
1554 dotdent[0].attr = ATTR_DIR | ATTR_ARCH;
1555
1556 memcpy(dotdent[1].name, ".. ", 8);
1557 memcpy(dotdent[1].ext, " ", 3);
1558 dotdent[1].attr = ATTR_DIR | ATTR_ARCH;
Heinrich Schuchardt00cf0762020-11-24 21:04:07 +01001559
1560 if (itr->is_root)
1561 set_start_cluster(mydata, &dotdent[1], 0);
1562 else
1563 set_start_cluster(mydata, &dotdent[1], itr->start_clust);
AKASHI Takahiro1c24b7b2018-09-11 15:59:10 +09001564
1565 ret = set_contents(mydata, retdent, 0, (__u8 *)dotdent,
1566 bytesperclust, &actwrite);
1567 if (ret < 0) {
1568 printf("Error: writing contents\n");
1569 goto exit;
1570 }
1571 /* Write twice for "." */
1572 set_start_cluster(mydata, &dotdent[0], START(retdent));
1573 ret = set_contents(mydata, retdent, 0, (__u8 *)dotdent,
1574 bytesperclust, &actwrite);
1575 if (ret < 0) {
1576 printf("Error: writing contents\n");
1577 goto exit;
1578 }
1579
1580 /* Flush fat buffer */
1581 ret = flush_dirty_fat_buffer(mydata);
1582 if (ret) {
1583 printf("Error: flush fat buffer\n");
1584 goto exit;
1585 }
1586
1587 /* Write directory table to device */
AKASHI Takahirod98c6742019-05-24 14:10:35 +09001588 ret = flush_dir(itr);
AKASHI Takahiro1c24b7b2018-09-11 15:59:10 +09001589 if (ret)
1590 printf("Error: writing directory entry\n");
1591
1592exit:
1593 free(dirname_copy);
1594 free(mydata->fatbuf);
1595 free(itr);
1596 free(dotdent);
1597 return ret;
1598}