blob: ea35f821161634f62ac24b5de9cf9e6ba7852c08 [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001/*
2 * file.c
3 *
4 * Include interface about file operation
5 * implementation file.
6 *
7 * Copyright (C) knightray@gmail.com
8 *
9 * SPDX-License-Identifier: GPL-2.0+
10 */
11
12/*
13 The file has been modified by antone@marvell.com (see "Marvell fixed")
14 in order to:
15 - achive best write performance
16 - fix cluster allocation boundary cases
17*/
18
19#include "file.h"
20#include "tffs.h"
21#include "common.h"
22#include "debug.h"
23#include "dir.h"
24#include "dirent.h"
25#include "fat.h"
26#include "hai.h"
27
28#ifndef DBG_FILE
29#undef DBG
30#define DBG nulldbg
31#endif
32
33/* private function declaration. */
34
35static BOOL
36_parse_open_mode(
37 IN byte * open_mode,
38 OUT uint32 * popen_mode
39);
40
41static void
42_file_seek(
43 IN tfile_t * pfile,
44 IN uint32 offset
45);
46
47static int32
48_initialize_file(
49 IN tffs_t * ptffs,
50 IN tdir_t * pdir,
51 IN tdir_entry_t * pdir_entry,
52 OUT tfile_t * pfile
53);
54
55static int32
56_get_next_sec(
57 IN tfile_t * pfile
58);
59
60/*----------------------------------------------------------------------------------------------------*/
61
62int32
63TFFS_fopen(
64 IN tffs_handle_t hfs,
65 IN byte * file_path,
66 IN byte * open_mode,
67 OUT tfile_handle_t * phfile)
68{
69 int32 ret;
70 tffs_t * ptffs = (tffs_t *)hfs;
71 byte * fname, * path;
72 byte * dup_file_path;
73 tfile_t * pfile;
74 tdir_t * pdir;
75 tdir_entry_t * pdir_entry;
76
77 if (!hfs || !file_path || !open_mode || !phfile)
78 return ERR_TFFS_INVALID_PARAM;
79
80 ret = TFFS_OK;
81 pfile = (tfile_t *)Malloc(sizeof(tfile_t));
82 dup_file_path = dup_string(file_path);
83 pdir_entry = dirent_malloc();
84 fname = (byte *)Malloc(DNAME_MAX);
85 pfile->secbuf = (ubyte *)Malloc(ptffs->pbs->byts_per_sec);
86 Memset(pfile->secbuf, 0, ptffs->pbs->byts_per_sec);
87
88 path = dup_file_path;
89 if (!divide_path(dup_file_path, fname)) {
90 ret = ERR_TFFS_INVALID_PATH;
91 goto _release;
92 }
93
94 if (!_parse_open_mode(open_mode, &pfile->open_mode)) {
95 ret = ERR_TFFS_INVALID_OPENMODE;
96 goto _release;
97 }
98
99 if ((dir_init(ptffs, path, &pdir)) != DIR_OK) {
100 ret = ERR_TFFS_INVALID_PATH;
101 goto _release;
102 }
103
104 if (dirent_find(pdir, fname, pdir_entry) != DIRENTRY_OK) {
105
106 DBG("%s(): can't find file [%s] at [%s]\n", __FUNCTION__, fname, path);
107 if (pfile->open_mode == OPENMODE_READONLY) {
108 ret = ERR_TFFS_FILE_NOT_EXIST;
109 goto _release;
110 }
111
112 if (!dirent_init(fname, 0, TRUE, pdir_entry)) {
113 ret = ERR_TFFS_INVALID_PATH;
114 goto _release;
115 }
116
117 if ((ret = dir_append_direntry(pdir, pdir_entry)) != DIR_OK) {
118 if (ret == ERR_DIR_NO_FREE_SPACE) {
119 ret = ERR_TFFS_NO_FREE_SPACE;
120 }
121 else {
122 ret = ERR_TFFS_DEVICE_FAIL;
123 }
124 goto _release;
125 }
126 }
127
128 ret = _initialize_file(ptffs, pdir, pdir_entry, pfile);
129 if (ret == FILE_OK) {
130 *phfile = (tfile_handle_t)pfile;
131 /* Marvell fixed: sync the dir sector, so changes to it are
132 possible while the file is open. Re-fetch and update on close */
133 dir_write_sector(pdir);
134 }
135
136_release:
137 Free(fname);
138 Free(dup_file_path);
139 return ret;
140}
141
142int32
143TFFS_rmfile(
144 IN tffs_handle_t hfs,
145 IN byte * file_path)
146{
147 int32 ret;
148 byte * fname, * path;
149 byte * dup_file_path;
150 tdir_t * pdir;
151 tffs_t * ptffs;
152 tdir_entry_t * pdir_entry;
153
154 ret = TFFS_OK;
155
156 if (!hfs || !file_path)
157 return ERR_TFFS_INVALID_PARAM;
158
159 ptffs = (tffs_t *)hfs;
160 dup_file_path = dup_string(file_path);
161 fname = (byte *)Malloc(DNAME_MAX);
162 pdir_entry = dirent_malloc();
163
164 path = dup_file_path;
165 if (!divide_path(dup_file_path, fname)) {
166 ret = ERR_TFFS_INVALID_PATH;
167 goto _release;
168 }
169
170 if ((dir_init(ptffs, path, &pdir)) != DIR_OK) {
171 ret = ERR_TFFS_INVALID_PATH;
172 goto _release;
173 }
174
175 if ((ret = dirent_find(pdir, fname, pdir_entry)) == DIRENTRY_OK) {
176 if (dirent_get_dir_attr(pdir_entry) & ATTR_DIRECTORY) {
177 ret = ERR_TFFS_IS_NOT_A_FILE;
178 goto _release;
179 }
180
181 if (dir_del_direntry(pdir, fname) != DIR_OK) {
182 ret = ERR_TFFS_REMOVE_FILE_FAIL;
183 goto _release;
184 }
185
186 if (fat_free_clus(pdir->ptffs->pfat, dirent_get_clus(pdir_entry)) != FAT_OK) {
187 ret = ERR_TFFS_REMOVE_FILE_FAIL;
188 goto _release;
189 }
190
191 ret = TFFS_OK;
192 }
193 else {
194 ret = ERR_TFFS_NO_SUCH_FILE;
195 goto _release;
196 }
197
198_release:
199 Free(fname);
200 Free(dup_file_path);
201 dirent_release(pdir_entry);
202 dir_destroy(pdir);
203 return ret;
204}
205
206int32
207file_write_bulk(
208 IN tfile_t * pfile,
209 IN uint32 *size,
210 IN ubyte *ptr);
211
212int32
213TFFS_fwrite(
214 IN tfile_handle_t hfile,
215 IN uint32 buflen,
216 IN ubyte * ptr)
217{
218 tfile_t * pfile = (tfile_t *)hfile;
219 tdir_entry_t * pdir_entry;
220 tdir_t * __attribute__ ((unused)) pdir;
221 uint32 __attribute__ ((unused)) write_size;
222 uint32 written_size;
223 uint32 __attribute__ ((unused)) file_size;
224 int32 ret;
225
226 if (!hfile || !ptr)
227 return ERR_TFFS_INVALID_PARAM;
228
229 if (pfile->open_mode == OPENMODE_READONLY)
230 return ERR_TFFS_READONLY;
231
232 pdir = pfile->pdir;
233 pdir_entry = pfile->pdir_entry;
234 file_size = dirent_get_file_size(pdir_entry);
235 write_size = buflen;
236 written_size = 0;
237
238 while (written_size < buflen) {
239 uint32 write_once_size;
240 uint32 remsize = buflen - written_size;
241
242 /* Marvell fixed */
243 if (!pfile->cur_sec_offset && (remsize>pfile->ptffs->pbs->byts_per_sec*2)) {
244 ret = file_write_bulk(pfile, &remsize, ptr + written_size);
245 if (ret != FILE_OK)
246 break; /* ret translated below is not used anyway */
247 written_size = buflen - remsize;
248 /* Marvell fixed: detection for new cluster is
249 based on cur_sec==sec_per_clus - automatic */
250 pfile->cur_sec++; /* next sector to write to */
251 continue; /* handle the remaining bytes if any */
252 }
253
254 write_once_size = min(pfile->ptffs->pbs->byts_per_sec - pfile->cur_sec_offset,
255 remsize);
256
257 Memcpy(pfile->secbuf + pfile->cur_sec_offset, ptr + written_size,
258 write_once_size);
259 written_size += write_once_size;
260 pfile->cur_sec_offset += write_once_size;
261
262 if (pfile->cur_sec_offset == pfile->ptffs->pbs->byts_per_sec) {
263 ret = file_write_sector(pfile);
264 if (ret == FILE_OK) {
265 /* Marvell fixed: detectionfor new cluster is
266 based on cur_sec==sec_per_clus - automatic */
267 pfile->cur_sec++; /* next sector to write to */
268 pfile->cur_sec_offset = 0;
269 }
270 else if (ret == ERR_FILE_NO_FREE_CLUSTER) {
271 ret = ERR_TFFS_NO_FREE_SPACE;
272 break;
273 }
274 else {
275 ret = ERR_TFFS_DEVICE_FAIL;
276 break;
277 }
278 }
279 }
280
281 /* Marvell fixed: Do not update on error, even if(written>0) */
282 if ((written_size > 0) && (ret == FILE_OK)) {
283 dirent_update_wrt_time(pdir_entry);
284 dirent_update_wrt_date(pdir_entry);
285 pfile->file_size += written_size;
286 }
287
288 return written_size;
289}
290
291
292
293int32
294TFFS_fread(
295 IN tfile_handle_t hfile,
296 IN uint32 buflen,
297 OUT ubyte * ptr)
298{
299 tfile_t * pfile = (tfile_t *)hfile;
300 tdir_entry_t * pdir_entry;
301 tdir_t * __attribute__ ((unused)) pdir;
302 uint32 read_size;
303 uint32 readin_size;
304 uint32 file_size;
305 int32 ret;
306
307 if (!hfile || !ptr)
308 return ERR_TFFS_INVALID_PARAM;
309
310 pdir = pfile->pdir;
311 pdir_entry = pfile->pdir_entry;
312 file_size = dirent_get_file_size(pdir_entry);
313 read_size = min(buflen, file_size - pfile->cur_fp_offset);
314 readin_size = 0;
315
316 while (readin_size < read_size) {
317
318 if (pfile->cur_sec_offset + (read_size - readin_size) >= pfile->ptffs->pbs->byts_per_sec) {
319
320 Memcpy(ptr + readin_size, pfile->secbuf + pfile->cur_sec_offset,
321 pfile->ptffs->pbs->byts_per_sec - pfile->cur_sec_offset);
322 readin_size += pfile->ptffs->pbs->byts_per_sec - pfile->cur_sec_offset;
323
324 ret = file_read_sector(pfile);
325 if (ret == FILE_OK) {
326 pfile->cur_sec_offset = 0;
327 continue;
328 }
329 else if (ret == ERR_FILE_EOF) {
330 //WARN("%s(): unexpect file end at %d\n", __FUNCTION__, __LINE__);
331 break;
332 }
333 else {
334 ERR("%s(): read file data sector failed at %d\n", __FUNCTION__, __LINE__);
335 ret = ERR_TFFS_DEVICE_FAIL;
336 goto _end;
337 }
338 }
339 else {
340 Memcpy(ptr + readin_size, pfile->secbuf + pfile->cur_sec_offset,
341 read_size - readin_size);
342 pfile->cur_sec_offset += (read_size - readin_size);
343 readin_size += (read_size - readin_size);
344 }
345 }
346
347 if (readin_size > 0) {
348 dirent_update_lst_acc_date(pdir_entry);
349 pfile->cur_fp_offset += readin_size;
350 ret = readin_size;
351 }
352 else {
353 ret = ERR_TFFS_FILE_EOF;
354 }
355
356_end:
357 return ret;
358}
359
360/* Marvell:
361 * Flush file buffer, update directory with size
362 * (like TFFS_fclose but without destroy)
363 */
364int32
365TFFS_fflush(
366 IN tfile_handle_t hfile)
367{
368 int32 ret;
369 tfile_t * pfile = (tfile_t *)hfile;
370
371 if (!hfile)
372 return ERR_TFFS_INVALID_PARAM;
373
374 ret = TFFS_OK;
375 /* Marvell fixed */
376 if (pfile->cur_sec_offset)
377 if (file_write_sector(pfile) != FILE_OK) {
378 return ERR_TFFS_DEVICE_FAIL;
379 }
380
381 dirent_set_file_size(pfile->pdir_entry, pfile->file_size);
382 if (dir_update_direntry(pfile->pdir, pfile->pdir_entry) != DIR_OK) {
383 ret = ERR_TFFS_DEVICE_FAIL;
384 }
385
386 return ret;
387}
388
389int32
390TFFS_fclose(
391 IN tfile_handle_t hfile)
392{
393 int32 ret;
394 tfile_t * pfile = (tfile_t *)hfile;
395
396 if (!hfile)
397 return ERR_TFFS_INVALID_PARAM;
398
399 ret = TFFS_fflush(hfile);
400
401 dir_destroy(pfile->pdir);
402 dirent_release(pfile->pdir_entry);
403 Free(pfile->secbuf);
404 Free(pfile);
405
406 return ret;
407}
408
409/*----------------------------------------------------------------------------------------------------*/
410
411int32
412file_read_sector(
413 IN tfile_t * pfile)
414{
415 int32 ret;
416 tffs_t * ptffs = pfile->ptffs;
417
418 if (pfile->cur_clus == 0) {
419 ret = ERR_FILE_EOF;
420 }
421 else {
422 if (fat_get_next_sec(ptffs->pfat, &pfile->cur_clus, &pfile->cur_sec)) {
423 if (cache_readsector(ptffs->pcache, clus2sec(ptffs, pfile->cur_clus) + pfile->cur_sec,
424 pfile->secbuf) == CACHE_OK) {
425 ret = FILE_OK;
426 }
427 else {
428 ret = ERR_FILE_DEVICE_FAIL;
429 }
430 }
431 else {
432 ret = ERR_FILE_EOF;
433 }
434 }
435
436 return ret;
437}
438
439int32
440file_write_sector(
441 IN tfile_t * pfile)
442{
443 int32 ret;
444 tffs_t * ptffs = pfile->ptffs;
445 tdir_entry_t * pdir_entry = pfile->pdir_entry;
446 uint32 new_clus;
447
448 ret = FILE_OK;
449 if (pfile->cur_clus == 0) {
450 int32 fatret;
451
452 if ((fatret = fat_malloc_clus(ptffs->pfat, FAT_INVALID_CLUS, &new_clus)) == FAT_OK) {
453 DBG("%s: new_clus = %d\n", __FUNCTION__, new_clus);
454 dirent_set_clus(pdir_entry, new_clus);
455 pfile->cur_clus = new_clus;
456 pfile->cur_sec = 0;
457 }
458 else if (fatret == ERR_FAT_NO_FREE_CLUSTER) {
459 return ERR_FILE_NO_FREE_CLUSTER;
460 }
461 else {
462 return ERR_FILE_DEVICE_FAIL;
463 }
464 }
465 /* Marvell fixed */
466 if (pfile->cur_sec >= ptffs->pbs->sec_per_clus) {
467 /* need a new cluster */
468 ret = _get_next_sec(pfile);
469 if (ret != FILE_OK) {
470 ERR("%s(): get next sector failed at %d with ret = %d\n", __FUNCTION__, __LINE__, ret);
471 return ret;
472 }
473 }
474
475 if (ret == FILE_OK) {
476 if (cache_writesector(ptffs->pcache, clus2sec(ptffs, pfile->cur_clus) + pfile->cur_sec,
477 pfile->secbuf) != CACHE_OK) {
478 ret = ERR_FILE_DEVICE_FAIL;
479 }
480 }
481
482 return ret;
483}
484/*
485 * Copyright (C) 2008 Marvell International Ltd. (antone@marvell.com)
486 */
487
488
489/* Marvell fixed - new function for better write throughput */
490int32
491file_write_bulk(
492 IN tfile_t * pfile,
493 IN uint32 *size,
494 IN ubyte *ptr)
495{
496 int32 ret;
497 tffs_t * ptffs = pfile->ptffs;
498 tdir_entry_t * pdir_entry = pfile->pdir_entry;
499 uint32 cur_clus;
500 uint32 nsectors = *size/pfile->ptffs->pbs->byts_per_sec;
501 uint32 remsectors = nsectors;
502 uint32 gotsectors;
503 uint32 len=0;
504 uint32 cur_sec;
505 ret = FILE_OK;
506
507 if (pfile->cur_clus == 0) {
508 int32 fatret;
509
510 if ((fatret = fat_malloc_clus(ptffs->pfat, FAT_INVALID_CLUS, &cur_clus)) == FAT_OK) {
511 DBG("%s: new_clus = %d\n", __FUNCTION__, cur_clus);
512 dirent_set_clus(pdir_entry, cur_clus);
513 pfile->cur_clus = cur_clus;
514 pfile->cur_sec = 0;
515 }
516 else if (fatret == ERR_FAT_NO_FREE_CLUSTER) {
517 return ERR_FILE_NO_FREE_CLUSTER;
518 }
519 else {
520 return ERR_FILE_DEVICE_FAIL;
521 }
522 }
523
524 if (pfile->cur_sec >= ptffs->pbs->sec_per_clus) {
525 /* need a new cluster */
526 ret = _get_next_sec(pfile);
527 if (ret != FILE_OK) {
528 ERR("%s(): get next sector failed at %d with ret = %d\n", __FUNCTION__, __LINE__, ret);
529 return ret;
530 }
531 }
532
533 cur_sec = pfile->cur_sec;
534 cur_clus = pfile->cur_clus;
535 gotsectors = 0;
536 for (remsectors = nsectors; remsectors; ) {
537 uint32 ns = ptffs->pbs->sec_per_clus - pfile->cur_sec;
538 uint32 prev_clus = pfile->cur_clus;
539 if (ns >= remsectors) {
540 ns = remsectors;
541 pfile->cur_sec += ns - 1; /* used as of now */
542 }
543 else {
544 pfile->cur_sec += ns - 1;
545 ret = _get_next_sec(pfile);
546 if (ret != FILE_OK) {
547 ERR("%s(): get next sector failed at %d with ret = %d\n", __FUNCTION__, __LINE__, ret);
548 ret = ERR_FILE_NO_FREE_CLUSTER;
549 break;
550 }
551 }
552 gotsectors += ns;
553 /* allocate clusters as long as the clusters are continuos */
554 if ((pfile->cur_clus != (prev_clus + 1)) || (ns == remsectors)) {
555 ret = HAI_writesectors(ptffs->hdev, clus2sec(ptffs, cur_clus) + cur_sec,
556 ptr + len, gotsectors);
557 if (ret != HAI_OK) {
558 ret = ERR_FILE_DEVICE_FAIL;
559 break;
560 }
561 len += gotsectors*pfile->ptffs->pbs->byts_per_sec;
562 /* Setup the next continuos chunk start point */
563 cur_clus = pfile->cur_clus;
564 cur_sec = pfile->cur_sec; /* relevant if remsectors>0 */
565 gotsectors = 0;
566 }
567 remsectors -= ns;
568 }
569 *size -= (nsectors-remsectors)*pfile->ptffs->pbs->byts_per_sec;
570 return ret;
571}
572/*
573 * Copyright (C) knightray@gmail.com
574 */
575
576
577/*----------------------------------------------------------------------------------------------------*/
578
579static int32
580_initialize_file(
581 IN tffs_t * ptffs,
582 IN tdir_t * pdir,
583 IN tdir_entry_t * pdir_entry,
584 OUT tfile_t * pfile)
585{
586 int32 ret;
587
588 pfile->ptffs = ptffs;
589 pfile->pdir = pdir;
590 pfile->pdir_entry = pdir_entry;
591 ret = FILE_OK;
592
593 pfile->start_clus = dirent_get_clus(pdir_entry);
594 pfile->file_size = dirent_get_file_size(pdir_entry);
595 pfile->cur_clus = pfile->start_clus;
596 pfile->cur_sec = 0;
597 pfile->cur_sec_offset = 0;
598 pfile->cur_fp_offset = 0;
599
600 if (pfile->open_mode == OPENMODE_APPEND) {
601 pfile->cur_fp_offset = dirent_get_file_size(pdir_entry);
602
603 _file_seek(pfile, pfile->cur_fp_offset);
604 if (pfile->cur_clus != 0) {
605 if (cache_readsector(ptffs->pcache, clus2sec(ptffs, pfile->cur_clus) + pfile->cur_sec,
606 pfile->secbuf) != CACHE_OK) {
607 ret = ERR_TFFS_DEVICE_FAIL;
608 }
609 }
610 }
611 else if (pfile->open_mode == OPENMODE_WRITE) {
612 if (pfile->start_clus != 0) {
613 if (fat_free_clus(ptffs->pfat, pfile->start_clus) != FAT_OK) {
614 ERR("%s(): %d fat_free_clus failed.\n", __FUNCTION__, __LINE__);
615 ret = ERR_TFFS_FAT;
616 }
617 }
618
619 dirent_set_file_size(pdir_entry , 0);
620 dirent_set_clus(pdir_entry, 0);
621
622 pfile->file_size = 0;
623 pfile->cur_clus = 0;
624 pfile->cur_sec = 0;
625 pfile->cur_sec_offset = 0;
626 pfile->cur_fp_offset = 0;
627 }
628 else {
629 if (pfile->cur_clus != 0) {
630 if (cache_readsector(ptffs->pcache, clus2sec(ptffs, pfile->cur_clus) + pfile->cur_sec,
631 pfile->secbuf) != CACHE_OK) {
632 ret = ERR_TFFS_DEVICE_FAIL;
633 }
634 }
635 }
636
637 return ret;
638}
639
640static void
641_file_seek(
642 IN tfile_t * pfile,
643 IN uint32 offset)
644{
645 int32 cur_offset;
646
647 cur_offset = offset;
648 while (cur_offset - pfile->ptffs->pbs->byts_per_sec > 0 &&
649 fat_get_next_sec(pfile->ptffs->pfat, &pfile->cur_clus, &pfile->cur_sec)) {
650 cur_offset -= pfile->ptffs->pbs->byts_per_sec;
651 }
652 pfile->cur_sec_offset = cur_offset;
653}
654
655static int32
656_get_next_sec(
657 IN tfile_t * pfile)
658{
659 int32 ret;
660 tffs_t * ptffs = pfile->ptffs;
661 uint32 new_clus;
662
663 ret = FILE_OK;
664
665 if (pfile->cur_sec + 1 < ptffs->pbs->sec_per_clus) {
666 pfile->cur_sec++;
667 }
668 else {
669 int32 fatret;
670
671 if ((fatret = fat_malloc_clus(ptffs->pfat, pfile->cur_clus, &new_clus)) == FAT_OK) {
672 pfile->cur_clus = new_clus;
673 pfile->cur_sec = 0;
674 }
675 else if (fatret == ERR_FAT_NO_FREE_CLUSTER) {
676 ret = ERR_FILE_NO_FREE_CLUSTER;
677 }
678 else {
679 ret = ERR_FILE_DEVICE_FAIL;
680 }
681 }
682
683 return ret;
684}
685
686static BOOL
687_parse_open_mode(
688 IN byte * open_mode,
689 OUT uint32 * popen_mode)
690{
691 if (!Strcmp(open_mode, "r")) {
692 *popen_mode = OPENMODE_READONLY;
693 }
694 else if (!Strcmp(open_mode, "w")) {
695 *popen_mode = OPENMODE_WRITE;
696 }
697 else if (!Strcmp(open_mode, "a")) {
698 *popen_mode = OPENMODE_APPEND;
699 }
700 else {
701 return FALSE;
702 }
703
704 return TRUE;
705}
706