blob: 07c5ca9b1c911e629c8f879f82deac7c8e107f21 [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001/*
2 * dirent.c
3 *
4 * Include functions implementing directory entry.
5 * implementation file.
6 *
7 * Copyright (C) knightray@gmail.com
8 *
9 * SPDX-License-Identifier: GPL-2.0+
10 */
11#include "dirent.h"
12#include "debug.h"
13#include "fat.h"
14#include "common.h"
15#include "dir.h"
16
17#ifndef DBG_DIRENT
18#undef DBG
19#define DBG nulldbg
20#endif
21
22/* Private routins declaration. */
23
24static int32
25_get_dirent(
26 IN tdir_t * pdir,
27 OUT dir_entry_t * pdirent
28);
29
30static void
31_parse_file_name(
32 IN tdir_t * pdir,
33 IN dir_entry_t * pdirent,
34 OUT tdir_entry_t * pdir_entry
35);
36
37static BOOL
38_get_long_file_name(
39 IN dir_entry_t * pdirent,
40 OUT byte * long_name
41);
42
43static BOOL
44_convert_short_fname(
45 IN ubyte * dir_name,
46 OUT byte * d_name
47);
48
49static BOOL
50_convert_to_short_fname(
51 IN byte * fname,
52 OUT ubyte * short_fname
53);
54
55static ubyte
56_get_check_sum(
57 IN ubyte * fname
58);
59
60/*----------------------------------------------------------------------------------------------------*/
61
62int32
63dirent_find(
64 IN tdir_t * pdir,
65 IN byte * dirname,
66 OUT tdir_entry_t * pdir_entry)
67{
68 int32 ret;
69
70 pdir->cur_clus = pdir->start_clus;
71 pdir->cur_sec = 0;
72 pdir->cur_dir_entry = 0;
73 if (dir_read_sector(pdir) != DIR_OK)
74 return ERR_DIRENTRY_NOT_FOUND;
75
76 ASSERT(pdir_entry->pdirent == NULL);
77 ret = DIRENTRY_OK;
78
79 while(1) {
80 dir_entry_t dirent;
81
82 if ((ret = _get_dirent(pdir, &dirent)) == DIRENTRY_OK) {
83
84 if (dirent.dir_name[0] == 0x00) {
85 ret = ERR_DIRENTRY_NOT_FOUND;
86 break;
87 }
88 else if (dirent.dir_name[0] == 0xE5) {
89 //FixMe do with 0x5
90 continue;
91 }
92
93 Memset(pdir_entry->long_name, 0, LONG_NAME_LEN);
94 Memset(pdir_entry->short_name, 0, SHORT_NAME_LEN);
95 _parse_file_name(pdir, &dirent, pdir_entry);
96
97 if (!Strcmp(pdir_entry->long_name, dirname)) {
98 break;
99 }
100 }
101 else if (ret == ERR_DIRENTRY_NOMORE_ENTRY){
102 ret = ERR_DIRENTRY_NOT_FOUND;
103 break;
104 }
105 else {
106 ret = ERR_DIRENTRY_DEVICE_FAIL;
107 break;
108 }
109 }
110
111 return ret;
112}
113
114BOOL
115dirent_is_empty(
116 IN tdir_t * pdir)
117{
118 BOOL ret;
119 tdir_entry_t * pdir_entry;
120
121 pdir->cur_clus = pdir->start_clus;
122 pdir->cur_sec = 0;
123 pdir->cur_dir_entry = 0;
124 if (dir_read_sector(pdir) != DIR_OK)
125 return ERR_DIRENTRY_NOT_FOUND;
126
127 pdir_entry = dirent_malloc();
128 ASSERT(pdir_entry->pdirent == NULL);
129 ret = TRUE;
130
131 while(1) {
132 dir_entry_t dirent;
133
134 if ((ret = _get_dirent(pdir, &dirent)) == DIRENTRY_OK) {
135
136 if (dirent.dir_name[0] == 0x00) {
137 break;
138 }
139 else if (dirent.dir_name[0] == 0xE5) {
140 //FixMe do with 0x5
141 continue;
142 }
143
144 Memset(pdir_entry->long_name, 0, LONG_NAME_LEN);
145 Memset(pdir_entry->short_name, 0, SHORT_NAME_LEN);
146 _parse_file_name(pdir, &dirent, pdir_entry);
147
148 if (!Strcmp(pdir_entry->long_name, ".") || !Strcmp(pdir_entry->long_name, "..")) {
149 continue;
150 }
151 else {
152 ret = FALSE;
153 break;
154 }
155 }
156 else if (ret == ERR_DIRENTRY_NOMORE_ENTRY){
157 break;
158 }
159 else {
160 ERR("%s(): get next direntry failed. ret = %d\n", __FUNCTION__, ret);
161 ret = FALSE;
162 break;
163 }
164 }
165
166 dirent_release(pdir_entry);
167 return ret;
168}
169
170/* Marvell fixed: when a new cluster is being allocated to a directory
171 the contents of all dirent's should be 0 */
172static void
173dirent_clear_new_cluster(tdir_t *pdir)
174{
175 Memset(pdir->secbuf, 0, pdir->ptffs->pbs->byts_per_sec);
176 for (pdir->cur_sec = 0;
177 pdir->cur_sec < pdir->ptffs->pbs->sec_per_clus;
178 pdir->cur_sec++)
179 if (dir_write_sector(pdir) != DIR_OK)
180 break;
181 pdir->cur_sec = 0;
182 pdir->cur_dir_entry = 0;
183 /* Leave secbuf as is, so no need to read it again */
184}
185
186/* Marvell fixed: when a new entry is appended at the end of a directory,
187all the entries should be allocated in the same sector, as they are later
188accessed in the sector buffer. This function:
189- checks if the required number of entries (dirent_num) is available;
190- otherwise, pads the current sector with E5 (deleted) entries,
191 and lets the search continue into the following sector. */
192static int32 dirent_space_check_extend(tdir_t *pdir, int32 dirent_num)
193{
194 int32 nent = pdir->ptffs->pbs->byts_per_sec / sizeof(dir_entry_t);
195 dir_entry_t *pde = (dir_entry_t *)pdir->secbuf;
196 int32 i, n0, ne5;
197 for (i = n0 = ne5 = 0; i < nent; i++) {
198 unsigned char c = pde[i].dir_name[0];
199 if (n0)
200 ASSERT(c == 0);
201 if (c == 0)
202 n0++;
203 else if (c == 0xE5)
204 ne5++;
205 else
206 n0 = ne5 = 0;
207 if ((n0 + ne5) == dirent_num) {
208 pdir->cur_dir_entry = i + 1 - dirent_num;
209 return DIRENTRY_OK;
210 }
211 }
212
213 /* not found the free entry sequence in this sector */
214 if (n0) {
215 /* replace 0 entries with E5 as dir continues to next sector */
216 for (i = nent - n0; i < nent; i++)
217 pde[i].dir_name[0] = 0xE5;
218 if (dir_write_sector(pdir) != DIR_OK)
219 return ERR_DIRENTRY_DEVICE_FAIL;
220 }
221 pdir->cur_dir_entry = nent; /* sector ended */
222 return ERR_DIRENTRY_NOMORE_ENTRY;
223}
224
225int32
226dirent_find_free_entry(
227 IN tdir_t * pdir,
228 IN tdir_entry_t * pdir_entry)
229{
230 int32 ret;
231 tffs_t * ptffs = pdir->ptffs;
232
233 ret = DIRENTRY_OK;
234
235 while(1) {
236 dir_entry_t dirent;
237
238 if ((ret = _get_dirent(pdir, &dirent)) == DIRENTRY_OK) {
239 /* Marvell fixed: revised search for free entries */
240 if (dirent_space_check_extend(pdir, pdir_entry->dirent_num) == DIRENTRY_OK)
241 break; /* enough free entries in this sector */
242 }
243 else if (ret == ERR_DIRENTRY_NOMORE_ENTRY) {
244 uint32 new_clus;
245
246 if (dir_write_sector(pdir) != DIR_OK) {
247 ret = ERR_DIRENTRY_DEVICE_FAIL;
248 break;
249 }
250 if ((ret = fat_malloc_clus(ptffs->pfat, pdir->cur_clus, &new_clus)) == FAT_OK) {
251 pdir->cur_clus = new_clus;
252 pdir->cur_sec = 0;
253 pdir->cur_dir_entry = 0;
254 /* Marvell fixed: zero the new cluster */
255 dirent_clear_new_cluster(pdir);
256 }
257 else {
258 ret = ERR_DIRENTRY_NOMORE_ENTRY;
259 break;
260 }
261 }
262 else {
263 break;
264 }
265 }
266
267 return ret;
268}
269
270int32
271dirent_get_next(
272 IN tdir_t * pdir,
273 OUT tdir_entry_t * pdir_entry)
274{
275 int32 ret;
276
277 ret = DIRENTRY_OK;
278
279 while(1) {
280 dir_entry_t dirent;
281
282 //print_sector(pdir->secbuf, 1);
283 DBG("%s():pdir->cur_clus = %d, pdir->cur_dir_entry = %d\n", __FUNCTION__, pdir->cur_clus, pdir->cur_dir_entry);
284 if ((ret = _get_dirent(pdir, &dirent)) == DIRENTRY_OK) {
285
286 if (dirent.dir_name[0] == 0x00) {
287 ret = ERR_DIRENTRY_NOMORE_ENTRY;
288 break;
289 }
290 else if (dirent.dir_name[0] == 0xE5) {
291 //FixMe do with 0x5
292 continue;
293 }
294
295 Memset(pdir_entry->long_name, 0, LONG_NAME_LEN);
296 Memset(pdir_entry->short_name, 0, SHORT_NAME_LEN);
297 _parse_file_name(pdir, &dirent, pdir_entry);
298
299 break;
300 }
301 else {
302 break;
303 }
304 }
305 return ret;
306}
307
308void
309dirent_release(
310 IN tdir_entry_t * pdir_entry)
311{
312 Free(pdir_entry->pdirent);
313 Free(pdir_entry);
314}
315
316tdir_entry_t *
317dirent_malloc()
318{
319 tdir_entry_t * pdir_entry;
320
321 pdir_entry = (tdir_entry_t *)Malloc(sizeof(tdir_entry_t));
322 Memset(pdir_entry, 0, sizeof(tdir_entry_t));
323 return pdir_entry;
324}
325
326BOOL
327dirent_init(
328 IN byte * fname,
329 IN ubyte dir_attr,
330 IN byte use_long_name,
331 OUT tdir_entry_t * pdir_entry)
332{
333 long_dir_entry_t * plfent;
334 dir_entry_t * pdirent;
335 uint32 lfent_num;
336 int32 lfent_i;
337 byte * pfname;
338
339 if (Strlen(fname) > LONG_NAME_LEN ||
340 (!use_long_name && Strlen(fname) > SHORT_NAME_LEN))
341 return FALSE;
342
343 if (use_long_name) {
344 lfent_num = Strlen(fname) / 13 + 2;
345 }
346 else {
347 lfent_num = 1;
348 }
349
350 pfname = fname;
351
352 plfent = (long_dir_entry_t *)Malloc(sizeof(long_dir_entry_t) * lfent_num);
353 Memset(plfent, 0, sizeof(long_dir_entry_t) * lfent_num);
354 pdirent = (dir_entry_t *)(&plfent[lfent_num - 1]);
355
356 _convert_to_short_fname(fname, pdirent->dir_name);
357 DBG("%s(): %s=>%s\n", __FUNCTION__, fname, pdirent->dir_name);
358 pdirent->dir_attr = dir_attr;
359 pdirent->dir_ntres = 0;
360 pdirent->dir_crt_time_tenth = dirent_get_cur_time_tenth();
361 pdirent->dir_crt_time = dirent_get_cur_time();
362 pdirent->dir_crt_date = dirent_get_cur_date();
363 pdirent->dir_lst_acc_date = pdirent->dir_crt_date;
364 pdirent->dir_wrt_time = pdirent->dir_crt_time;
365 pdirent->dir_wrt_date = pdirent->dir_crt_date;
366 pdirent->dir_fst_clus_hi = 0;
367 pdirent->dir_fst_clus_lo = 0;
368 pdirent->dir_file_size = 0;
369
370 if (use_long_name) {
371 for (lfent_i = lfent_num - 2; lfent_i >= 0; lfent_i--) {
372 ubyte fname_line[13];
373
374 Memset(fname_line, 0xFF, 13);
375 Memcpy(fname_line, pfname, min(fname + Strlen(fname) - pfname + 1, 13));
376
377 if (lfent_i == 0) {
378 plfent[lfent_i].ldir_ord = (lfent_num - 1 - lfent_i) | LAST_LONG_ENTRY;
379 }
380 else {
381 plfent[lfent_i].ldir_ord = lfent_num - 1 - lfent_i;
382 }
383 copy_to_unicode(fname_line, 5, plfent[lfent_i].ldir_name1);
384 copy_to_unicode(fname_line + 5, 6, plfent[lfent_i].ldir_name2);
385 copy_to_unicode(fname_line + 11, 2, plfent[lfent_i].ldir_name3);
386 pfname += 13;
387
388 plfent[lfent_i].ldir_attr = ATTR_LONG_NAME;
389 plfent[lfent_i].ldir_type = 0;
390 plfent[lfent_i].ldir_chksum = _get_check_sum(pdirent->dir_name);
391 plfent[lfent_i].ldir_fst_clus_lo = 0;
392 }
393 }
394
395 pdir_entry->pdirent = (dir_entry_t *)plfent;
396 pdir_entry->dirent_num = lfent_num;
397 Strcpy(pdir_entry->long_name, fname);
398 _convert_short_fname(pdirent->dir_name, pdir_entry->short_name);
399
400 return TRUE;
401}
402
403uint16
404dirent_get_cur_time()
405{
406 tffs_sys_time_t curtm;
407 uint16 ret;
408
409 Getcurtime(&curtm);
410
411 ret = 0;
412 ret |= (curtm.tm_sec >> 2) & 0x1F;
413 ret |= (curtm.tm_min << 5) & 0x7E0;
414 ret |= (curtm.tm_hour << 11) & 0xF800;
415
416 return ret;
417}
418
419uint16
420dirent_get_cur_date()
421{
422 tffs_sys_time_t curtm;
423 uint16 ret;
424
425 Getcurtime(&curtm);
426
427 ret = 0;
428 ret |= (curtm.tm_mday) & 0x1F;
429 ret |= ((curtm.tm_mon + 1) << 5) & 0x1E0;
430 ret |= ((curtm.tm_year - 80) << 9) & 0xFE00;
431
432 return ret;
433}
434
435ubyte
436dirent_get_cur_time_tenth()
437{
438 tffs_sys_time_t curtm;
439 uint16 ret;
440
441 Getcurtime(&curtm);
442
443 ret = (curtm.tm_sec & 1) == 0 ? 0 : 100;
444 return ret;
445}
446
447/*----------------------------------------------------------------------------------------------------*/
448
449static int32
450_get_dirent(
451 IN tdir_t * pdir,
452 OUT dir_entry_t * pdirent)
453{
454 int32 ret;
455
456 ret = DIRENTRY_OK;
457
458 if (pdir->cur_dir_entry <
459 (pdir->ptffs->pbs->byts_per_sec / sizeof(dir_entry_t))) {
460 Memcpy(pdirent, (dir_entry_t *)pdir->secbuf + pdir->cur_dir_entry, sizeof(dir_entry_t));
461 pdir->cur_dir_entry++;
462 }
463 else {
464 if (fat_get_next_sec(pdir->ptffs->pfat, &pdir->cur_clus, &pdir->cur_sec)) {
465 pdir->cur_dir_entry = 0;
466 if ((ret = dir_read_sector(pdir)) == DIR_OK) {
467 Memcpy(pdirent, (dir_entry_t *)pdir->secbuf + pdir->cur_dir_entry, sizeof(dir_entry_t));
468 pdir->cur_dir_entry++;
469 }
470 else {
471 ret = ERR_DIRENTRY_DEVICE_FAIL;
472 }
473 }
474 else {
475 ret = ERR_DIRENTRY_NOMORE_ENTRY;
476 }
477 }
478
479 return ret;
480}
481
482static void
483_parse_file_name(
484 IN tdir_t * pdir,
485 IN dir_entry_t * pdirent,
486 OUT tdir_entry_t * pdir_entry)
487{
488 int32 lf_entry_num;
489
490 /*
491 * Marvell fixed: pdir_entry->pdirent is allocated below, while
492 * the function is called in a loop, e.g. in dirent_find(), so
493 * the previously allocated memory should be released to prevent
494 * a memory leak: with a lot of files in the working dir, searches
495 * exhausted the heap and following allocations failed.
496 */
497 if (pdir_entry->pdirent) {
498 /* This is 0 after dirent_init() unless object re-used */
499 Free(pdir_entry->pdirent);
500 pdir_entry->pdirent = NULL;
501 }
502 lf_entry_num = 0;
503 /* Marvell fixed: was "& ATTR_LONG_NAME", however one of the bits
504 is ATTR_VOLUME_ID, which alone indicates a volume id, short */
505 if (pdirent->dir_attr == ATTR_LONG_NAME) {
506 uint32 lf_i;
507 dir_entry_t dirent;
508
509 lf_entry_num = pdirent->dir_name[0] & ~(LAST_LONG_ENTRY);
510 pdir_entry->pdirent = (dir_entry_t *)Malloc((lf_entry_num + 1) * sizeof(dir_entry_t));
511
512 _get_long_file_name(pdirent, pdir_entry->long_name + (lf_entry_num - 1) * 13);
513 Memcpy(pdir_entry->pdirent, pdirent, sizeof(dir_entry_t));
514
515 for (lf_i = 1; lf_i < lf_entry_num; lf_i++) {
516 _get_dirent(pdir, &dirent);
517 Memcpy(pdir_entry->pdirent + lf_i, &dirent, sizeof(dir_entry_t));
518 _get_long_file_name(&dirent, pdir_entry->long_name + (lf_entry_num - lf_i - 1) * 13);
519 }
520
521 _get_dirent(pdir, &dirent);
522 Memcpy(pdir_entry->pdirent + lf_i, &dirent, sizeof(dir_entry_t));
523 }
524 else {
525 pdir_entry->pdirent = (dir_entry_t *)Malloc(sizeof(dir_entry_t));
526
527 _convert_short_fname(pdirent->dir_name, pdir_entry->long_name);
528 Memcpy(pdir_entry->pdirent, pdirent, sizeof(dir_entry_t));
529 }
530
531 _convert_short_fname(pdirent->dir_name, pdir_entry->short_name);
532 pdir_entry->dirent_num = lf_entry_num + 1;
533}
534
535static ubyte
536_get_check_sum(
537 IN ubyte * fname)
538{
539 int16 fname_len;
540 ubyte sum;
541
542 sum = 0;
543 for (fname_len = 11; fname_len != 0; fname_len--) {
544 sum = ((sum & 1) ? 0x80 : 0) + (sum >> 1) + *fname++;
545 }
546 return sum;
547}
548
549static BOOL
550_convert_to_short_fname(
551 IN byte * fname,
552 OUT ubyte * short_fname)
553{
554 byte * pfname;
555 byte * pcur;
556 uint16 sf_i;
557 static uint16 num_tail = 0;
558
559 for (sf_i = 0; sf_i < 11; sf_i++)
560 short_fname[sf_i] = ' ';
561
562 if (!Strcmp(fname, ".") || !Strcmp(fname, "..")) {
563 /* Marvell fixed: names should not be 0-terminated */
564 Memcpy((byte *)short_fname, fname, Strlen(fname));
565 return TRUE;
566 }
567
568 pfname = dup_string(fname);
569 pcur = pfname;
570 sf_i = 0;
571
572 trip_blanks(pfname);
573
574 while (sf_i < 8) {
575 if (*pcur == '\0' || *pcur == '.') {
576 break;
577 }
578 short_fname[sf_i++] = Toupper(*pcur++);
579 }
580
581 if (*pcur == '.') {
582 pcur++;
583 }
584 else {
585 if (*pcur != '\0') {
586 byte str_tail[8];
587
588 while(*pcur && *pcur != '.')
589 pcur++;
590
591 if (*pcur == '.')
592 pcur++;
593
594 Sprintf(str_tail, "~%d", num_tail++);
595 Memcpy(short_fname + (8 - Strlen(str_tail)), str_tail, Strlen(str_tail));
596
597 if (*pcur == '\0')
598 goto _release;
599 }
600 }
601
602 sf_i = 8;
603
604 while (sf_i < 11) {
605 if (*pcur == '\0')
606 break;
607 short_fname[sf_i++] = Toupper(*pcur++);
608 }
609
610_release:
611 Free(pfname);
612 return TRUE;
613}
614
615static BOOL
616_get_long_file_name(
617 IN dir_entry_t * pdirent,
618 OUT byte * long_name)
619{
620 long_dir_entry_t * pldirent;
621
622 pldirent = (long_dir_entry_t *)pdirent;
623
624 copy_from_unicode(pldirent->ldir_name1, 5, long_name);
625 copy_from_unicode(pldirent->ldir_name2, 6, long_name + 5);
626 copy_from_unicode(pldirent->ldir_name3, 2, long_name + 11);
627 return TRUE;
628}
629
630static BOOL
631_convert_short_fname(
632 IN ubyte * dir_name,
633 OUT byte * d_name)
634{
635 uint32 i;
636
637 Memset(d_name, 0, 11);
638 for (i = 0; i < 8; i++) {
639 if (dir_name[i] == ' ')
640 break;
641 d_name[i] = dir_name[i];
642 }
643
644 if (dir_name[8] != ' ') {
645 uint32 j;
646
647 d_name[i++] = '.';
648 for (j = 0; j < 3; j++) {
649 if (dir_name[8 + j] == ' ')
650 break;
651 d_name[i + j] = dir_name[8 + j];
652 }
653 }
654 return TRUE;
655}
656