1 /*
2 * Copyright (c) 2013-2019 Huawei Technologies Co., Ltd. All rights reserved.
3 * Copyright (c) 2020-2021 Huawei Device Co., Ltd. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without modification,
6 * are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this list of
9 * conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice, this list
12 * of conditions and the following disclaimer in the documentation and/or other materials
13 * provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its contributors may be used
16 * to endorse or promote products derived from this software without specific prior written
17 * permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include "ffconf.h"
33 #include "virpartff.h"
34 #include "string.h"
35 #include "diskio.h"
36
37 #ifdef LOSCFG_FS_FAT_VIRTUAL_PARTITION
38
39 #if FF_FS_REENTRANT
40 #if FF_USE_LFN == 1
41 #error Static LFN work area cannot be used at thread-safe configuration
42 #endif
43 #define ENTER_FF(fs) do { if (!lock_fs(fs)) return FR_TIMEOUT; } while (0)
44 #define LEAVE_FF(fs, res) do { unlock_fs(fs, res); return res; } while (0)
45 #else
46 #define ENTER_FF(fs)
47 #define LEAVE_FF(fs, res) return (res)
48 #endif
49
50 extern FATFS *FatFs[FF_VOLUMES];
51
52 /*
53 * follow_virentry:
54 * Compare the top segment with the virtual partition entry and replace it to its CHILD FATFS
55 *
56 * Acceptable return value:
57 * - FR_OK : The top segment matches one of the virtual partition entries, and the FATFS
58 * has been replaced to the corresponding FATFS.
59 * - FR_DENIED : The top segment does not matched any of the virtual partition entries, and
60 * the FATFS has kept as it is. Or the virtual partition feature has been
61 * switched down by any reason.
62 * - FR_INT_ERR : Assertion error
63 */
follow_virentry(FFOBJID * obj,const TCHAR * path)64 FRESULT follow_virentry(FFOBJID *obj, const TCHAR *path)
65 {
66 TCHAR keyword[FF_MAX_LFN + 1] = {0};
67 FATFS *fs = obj->fs;
68 INT len;
69 UINT i;
70
71 (void)memset_s(keyword, sizeof(keyword), 0, sizeof(keyword));
72 /* Search and copy the first segment in path */
73 for (len = 0; *path != '/' && *path != '\\' && *path != '\0' && len < FF_MAX_LFN; path++, len++) {
74 keyword[len] = *path;
75 }
76
77 if (len == 0 || len > _MAX_ENTRYLENGTH) {
78 return FR_DENIED;
79 }
80
81 /*
82 * Compare the segment does match one for virtual partitions' entry or not,
83 * replace the FATFS if the result is positive
84 */
85 for (i = 0; i < fs->vir_amount; i++) {
86 if (!CHILDFS(fs, i)) {
87 return FR_INT_ERR;
88 }
89 if (memcmp((CHILDFS(fs, i))->namelabel, keyword, _MAX_ENTRYLENGTH + 1) == 0) {
90 obj->fs = CHILDFS(fs, i);
91 return FR_OK;
92 }
93 }
94
95 return FR_DENIED;
96 }
97
f_checkname(const TCHAR * path)98 FRESULT f_checkname(const TCHAR *path)
99 {
100 FRESULT res;
101 DIR dj;
102 FATFS fs;
103 TCHAR *label = (TCHAR *)path;
104 DEF_NAMBUF
105
106 (void)memset_s(&fs, sizeof(fs), 0, sizeof(fs));
107 dj.obj.fs = &fs;
108 INIT_NAMBUF(&fs);
109 res = create_name(&dj, &path);
110 /* the last byte of file name can't be a space */
111 if (res == FR_OK && dj.fn[11] == 0x20) {
112 res = FR_INVALID_NAME;
113 return res;
114 }
115
116 FREE_NAMBUF();
117
118 for (; *label != '\0'; label++) {
119 if (label - path > _MAX_ENTRYLENGTH) {
120 res = FR_INVALID_NAME;
121 return res;
122 }
123 if (*label == '/' || *label == '\\') {
124 res = FR_INVALID_NAME;
125 return res;
126 }
127 }
128 return res;
129 }
130
f_getfatfs(int vol)131 FATFS *f_getfatfs(int vol)
132 {
133 FATFS *fs = NULL;
134 if (vol < 0 || vol >= FF_VOLUMES) {
135 fs = NULL;
136 } else {
137 fs = FatFs[vol];
138 }
139 return fs;
140 }
141
FatfsCheckBoundParam(FATFS * fs,DWORD clust)142 static FRESULT FatfsCheckBoundParam(FATFS *fs, DWORD clust)
143 {
144 if (fs->st_clst <= 2 || (fs->st_clst + fs->ct_clst) > fs->n_fatent) {
145 return FR_INT_ERR;
146 }
147 if (clust < 2 || clust > fs->n_fatent) {
148 return FR_INT_ERR;
149 }
150 if (clust >= (fs->st_clst + fs->ct_clst) || clust < fs->st_clst) {
151 return FR_CHAIN_ERR;
152 }
153
154 return FR_OK;
155 }
156
157 /*
158 * check_boundary:
159 * Check the chain occupied more than one virtual partition or not start at the var 'clust'
160 *
161 * Acceptable Return Value:
162 * - FR_OK : The chain occupied only one virtual parition
163 * - FR_CHAIN_ERR : The chain occupied more than one virtual parition
164 */
f_boundary(FATFS * fs,DWORD clust)165 FRESULT f_boundary(FATFS *fs, DWORD clust)
166 {
167 FFOBJID obj;
168 FRESULT res;
169 obj.fs = fs;
170 if (fs == NULL) {
171 return FR_INT_ERR;
172 }
173 if (fs->fs_type != FS_FAT32) {
174 return FR_INVAILD_FATFS;
175 }
176 ENTER_FF(fs);
177
178 res = FatfsCheckBoundParam(fs, clust);
179 if (res != FR_OK) {
180 LEAVE_FF(fs, res);
181 }
182 for (;;) {
183 clust = get_fat(&obj, clust);
184 if (clust == 0xFFFFFFFF) {
185 LEAVE_FF(fs, FR_DISK_ERR);
186 }
187 if (clust == 0x0FFFFFFF) {
188 break;
189 }
190 if (clust < 2 || clust >= fs->n_fatent) {
191 LEAVE_FF(fs, FR_INT_ERR);
192 }
193 if (clust >= (fs->st_clst + fs->ct_clst) || clust < fs->st_clst) {
194 LEAVE_FF(fs, FR_CHAIN_ERR);
195 }
196 }
197
198 LEAVE_FF(fs, FR_OK);
199 }
200
201 /*
202 * f_unregvirfs:
203 * Uninitialized the CHILD FATFS object
204 *
205 * Acceptable Return Value:
206 * - FR_OK : Successfully initialized the CHILD FATFS object.
207 */
f_disvirfs(FATFS * fs)208 FRESULT f_disvirfs(FATFS *fs)
209 {
210 if (ISCHILD(fs)) {
211 return FR_INVAILD_FATFS;
212 }
213
214 if (fs->vir_amount > _MAX_VIRVOLUMES) {
215 return FR_INT_ERR;
216 }
217
218 ENTER_FF(fs);
219
220 (void)f_unregvirfs(fs);
221 LEAVE_FF(fs, FR_OK);
222 }
223
f_unregvirfs(FATFS * fs)224 FRESULT f_unregvirfs(FATFS *fs)
225 {
226 UINT i;
227
228 if (fs == NULL || ISCHILD(fs)) {
229 return FR_INVAILD_FATFS;
230 }
231
232 fs->vir_avail = FS_VIRDISABLE;
233 /* PARENT FATFS has linked to CHILD FATFS already */
234 if (fs->child_fs != NULL) {
235 /* Followed the CHILD FATFS and free the memory */
236 for (i = 0; i < fs->vir_amount; i++) {
237 if (CHILDFS(fs, i) != NULL) {
238 ff_memfree(CHILDFS(fs, i));
239 }
240 }
241 /* Free the 'child_fs' feild */
242 ff_memfree(fs->child_fs);
243 fs->child_fs = NULL;
244 fs->vir_amount = 0xFFFFFFFF;
245 }
246
247 return FR_OK;
248 }
249
FatfsSetParentFs(FATFS * pfs,FATFS * fs)250 static void FatfsSetParentFs(FATFS *pfs, FATFS *fs)
251 {
252 pfs->fs_type = fs->fs_type; /* Copy the feild info from PARENT FATFS object */
253 pfs->pdrv = fs->pdrv;
254 pfs->n_fats = fs->n_fats;
255 pfs->id = fs->id;
256 pfs->n_rootdir = fs->n_rootdir;
257 pfs->csize = fs->csize;
258 #if FF_MAX_SS != FF_MIN_SS
259 pfs->ssize = fs->ssize;
260 #endif
261 pfs->sobj = fs->sobj;
262
263 #if FF_FS_RPATH != 0
264 pfs->cdir = 0;
265 #endif
266 pfs->n_fatent = fs->n_fatent;
267 pfs->fsize = fs->fsize;
268 pfs->volbase = fs->volbase;
269 pfs->fatbase = fs->fatbase;
270 pfs->dirbase = fs->dirbase;
271 pfs->database = fs->database;
272 pfs->last_clst = 0xFFFFFFFF; /* Mark the 'last_clst' and 'free_clst' in CHILD FATFS is not been updated for now */
273 pfs->free_clst = 0xFFFFFFFF;
274 pfs->st_clst = 0xFFFFFFFF; /* Mark the 'st_clst' and 'ct_clst' in CHILD FATFS is not been update for now. */
275 pfs->ct_clst = 0xFFFFFFFF;
276 pfs->vir_flag = FS_CHILD; /* Mark the FATFS object is a CHILD */
277 pfs->vir_avail = FS_VIRENABLE; /* Mark the CHILD object is enable for now */
278 pfs->parent_fs = (void *)fs; /* Link to the PARENT object */
279 pfs->child_fs = (void *)NULL; /* Link the unrelated feild to NULL */
280 }
281
282 /*
283 * f_regvirfs:
284 * Initialized the CHILD FATFS object
285 *
286 * Acceptable Return Value:
287 * - FR_OK : Successfully initialized the CHILD FATFS object.
288 *
289 * Others Return Value:
290 * - FR_INVAILD_FATFS : The FATFS object has error or the info in it has been occuried
291 * - FR_DENIED : The virtual partition feature has been shut down by switcher
292 * - FR_DISK_ERR : A disk error happened
293 * - FR_NOT_ENOUGH_CORE : Not enough memory for allocate space for CHILD FATFS
294 * - FR_INVALID_PARAMETER : There is a invalid value in current setting
295 */
f_regvirfs(FATFS * fs)296 FRESULT f_regvirfs(FATFS *fs)
297 {
298 UINT i;
299 FATFS *pfs = NULL;
300
301 if (fs == NULL || ISCHILD(fs)) {
302 return FR_INVAILD_FATFS;
303 }
304
305 if (fs->vir_amount > _MAX_VIRVOLUMES) {
306 return FR_INT_ERR;
307 }
308
309 fs->parent_fs = (void *)fs; /* Relink to itself */
310 /* Mark the FATFS object is PARENT */
311 fs->st_clst = 0xFFFFFFFF;
312 fs->ct_clst = 0xFFFFFFFF;
313 /* Allocate a space for linking to the child FATFS */
314 fs->child_fs = (void **)ff_memalloc(fs->vir_amount * sizeof(void *));
315 if (fs->child_fs == NULL) {
316 return FR_NOT_ENOUGH_CORE;
317 }
318 fs->vir_avail = FS_VIRENABLE; /* Mark the PARENT object is enable for now */
319
320 /* Set the CHILD object field */
321 for (i = 0; i < fs->vir_amount; i++) {
322 pfs = ff_memalloc(sizeof(FATFS)); /* Allocate a memory for current child FATFS object */
323 if (pfs == NULL) { /* If allocate failed, must call 'f_unregvirfs' to free the previous FATFS object memory */
324 goto ERROUT;
325 }
326 FatfsSetParentFs(pfs, fs);
327 *(fs->child_fs + i) = (void *)pfs;
328 }
329
330 return FR_OK;
331 ERROUT:
332 while (i > 0) {
333 --i;
334 ff_memfree(*(fs->child_fs + i));
335 }
336 ff_memfree(fs->child_fs);
337 fs->child_fs = NULL;
338
339 return FR_NOT_ENOUGH_CORE;
340 }
341
FatfsCheckScanFatParam(FATFS * fs)342 static FRESULT FatfsCheckScanFatParam(FATFS *fs)
343 {
344 if (fs == NULL) {
345 return FR_INVAILD_FATFS;
346 }
347
348 if (ISNORMAL(fs)) {
349 return FR_DENIED;
350 }
351
352 if (fs->fs_type != FS_FAT32 || ISPARENT(fs)) {
353 return FR_INVAILD_FATFS;
354 }
355
356 if (fs->st_clst < 3 || fs->st_clst >= fs->n_fatent) {
357 return FR_INVAILD_FATFS;
358 }
359
360 if (fs->ct_clst == 0 || fs->ct_clst > (fs->n_fatent - 3)) {
361 return FR_INVAILD_FATFS;
362 }
363
364 if ((fs->st_clst + fs->ct_clst) > fs->n_fatent || (fs->st_clst + fs->ct_clst) < 3) {
365 return FR_INVAILD_FATFS;
366 }
367
368 return FR_OK;
369 }
370
371 /*
372 * f_scanfat:
373 * Scan the FAT inside the boundary of CHILD FATFS limit, and update the free cluster and last cluster
374 *
375 * Acceptable Return Value:
376 * - FR_OK : Successfully scanned the FAT and update field.
377 *
378 * Others Return Value:
379 * - FR_INVAILD_FATFS : The FATFS object has error or the info in it has been occuried
380 * - FR_DENIED : The virtual partition feature has been shut down by switcher
381 * - FR_DISK_ERR : A disk error happened
382 */
f_scanfat(FATFS * fs)383 FRESULT f_scanfat(FATFS *fs)
384 {
385 FRESULT res;
386 DWORD clst;
387 DWORD link;
388 FFOBJID obj;
389
390 res = FatfsCheckScanFatParam(fs);
391 if (res != FR_OK) {
392 return res;
393 }
394
395 ENTER_FF(fs);
396 res = FR_OK;
397 obj.fs = fs;
398
399 fs->free_clst = fs->ct_clst;
400 for (clst = fs->st_clst; clst < fs->st_clst + fs->ct_clst; clst++) {
401 link = get_fat(&obj, clst);
402 if (link == 0xFFFFFFFF) {
403 LEAVE_FF(fs, FR_DISK_ERR);
404 }
405 if (link == 0) {
406 continue;
407 }
408 fs->free_clst--;
409 }
410 fs->last_clst = fs->st_clst - 1;
411
412 LEAVE_FF(fs, res);
413 }
414
FatfsCheckStart(BYTE * work,FATFS * fs,BYTE vol)415 static FRESULT FatfsCheckStart(BYTE *work, FATFS *fs, BYTE vol)
416 {
417 DWORD startBaseSect, countBaseSect;
418
419 countBaseSect = LD2PC(vol); /* Volume Base Sectors Count */
420 startBaseSect = LD2PS(vol); /* Volume Base Start Sector */
421
422 /* Check ASCII for Keyword "LITE" */
423 if (ld_dword(work + VR_VertifyString) != 0x4C495445) {
424 return FR_NOVIRPART;
425 }
426 /* Check whether filesystem has been changed or not */
427 if (work[VR_PartitionFSType] != fs->fs_type) {
428 return FR_MODIFIED;
429 }
430 /* Check whether volume base sector has benn changed or not */
431 if (ld_dword(work + VR_PartitionStSec) != startBaseSect) {
432 return FR_MODIFIED;
433 }
434 /* Check whether volume base size hase been changed or not */
435 if (ld_dword(work + VR_PartitionCtSec) != countBaseSect) {
436 return FR_MODIFIED;
437 }
438 /* Check whether volume cluster size has been changed or not */
439 if (ld_word(work + VR_PartitionClstSz) != fs->csize) {
440 return FR_MODIFIED;
441 }
442 /* Check whether volume start cluster is cluster #3 or not */
443 if (ld_dword(work + VR_PartitionCtClst) != fs->n_fatent) {
444 return FR_MODIFIED;
445 }
446 /* Check whether virtual partition overlimit */
447 if (work[VR_PartitionCnt] > _MAX_VIRVOLUMES) {
448 return FR_MODIFIED;
449 }
450
451 return FR_OK;
452 }
453
FatfsCheckPercent(FATFS * fs,WORD i)454 static FRESULT FatfsCheckPercent(FATFS *fs, WORD i)
455 {
456 if ((CHILDFS(fs, i))->st_clst + (CHILDFS(fs, i))->ct_clst < fs->n_fatent) {
457 fs->st_clst = (CHILDFS(fs, i))->st_clst + (CHILDFS(fs, i))->ct_clst;
458 fs->ct_clst = fs->n_fatent - ((CHILDFS(fs, i))->st_clst + (CHILDFS(fs, i))->ct_clst);
459 } else if ((CHILDFS(fs, i))->st_clst + (CHILDFS(fs, i))->ct_clst == fs->n_fatent) {
460 fs->st_clst = 0xFFFFFFFF;
461 fs->ct_clst = 0xFFFFFFFF;
462 } else {
463 (void)f_unregvirfs(fs);
464 return FR_MODIFIED;
465 }
466
467 return FR_OK;
468 }
469
FatfsCheckPartClst(FATFS * fs,WORD i)470 static FRESULT FatfsCheckPartClst(FATFS *fs, WORD i)
471 {
472 if (i == 0) {
473 /* First virtual partition must start at cluster #3 */
474 if ((CHILDFS(fs, i))->st_clst != 3) {
475 (void)f_unregvirfs(fs);
476 return FR_MODIFIED;
477 }
478 } else {
479 /* Check whether the current virtual partition is closely next to the previous virtual partition */
480 if ((CHILDFS(fs, i))->st_clst != (CHILDFS(fs, (i - 1))->st_clst + CHILDFS(fs, (i - 1))->ct_clst)) {
481 (void)f_unregvirfs(fs);
482 return FR_MODIFIED;
483 }
484 }
485
486 return FR_OK;
487 }
488
FatfsSetChildClst(BYTE * work,FATFS * fs,WORD i)489 static void FatfsSetChildClst(BYTE *work, FATFS *fs, WORD i)
490 {
491 (CHILDFS(fs, i))->st_clst = ld_dword(work + VR_PARTITION + i * VR_ITEMSIZE + VR_StartClust);
492 (CHILDFS(fs, i))->ct_clst = ld_dword(work + VR_PARTITION + i * VR_ITEMSIZE + VR_CountClust);
493 }
494
495 /*
496 * f_ckvtlpt :
497 * Check the external SD virtual paritition sectors and read configure from it
498 *
499 * Acceptable Return Value:
500 * - FR_OK : The external SD configure is complete, all info has been set to the
501 * each CHILD FATFS
502 * - FR_NOT_MATCHED : The virtual partition's configure does not matched as current setting
503 * - FR_MODIFIED : The virtual partition's configure has been destroyed partly or completely
504 * - FR_NOVIRPART : The external SD has not been apllied as virtual partition yet
505 *
506 * Others Return Value:
507 * - FR_INVAILD_FATFS : The FATFS object has error or the info in it has been occuried
508 * - FR_DENIED : The virtual partition feature has been shut down by switcher
509 * - FR_INVALID_DRIVE : The drive index is error
510 * - FR_DISK_ERR : A Disk error happened
511 */
f_checkvirpart(FATFS * fs,const TCHAR * path,BYTE vol)512 FRESULT f_checkvirpart(FATFS *fs, const TCHAR *path, BYTE vol)
513 {
514 FRESULT res;
515 WORD i;
516 DWORD virSect;
517 DWORD tmp;
518 BYTE pdrv;
519 BYTE *work = NULL;
520 CHAR label[_MAX_ENTRYLENGTH + 1];
521 DWORD *labelTmp = NULL; /* to clear the compilation warning */
522
523 if (fs == NULL || (disk_status(fs->pdrv) & STA_NOINIT)) {
524 return FR_INVAILD_FATFS; /* The object is invalid */
525 }
526
527 /* Lock the filesystem object */
528 res = mount_volume(&path, &fs, FA_WRITE); /* Update the filesystem info to the parent fs */
529 if (res != FR_OK) {
530 LEAVE_FF(fs, res);
531 }
532
533 if (ISCHILD(fs)) {
534 LEAVE_FF(fs, FR_INT_ERR);
535 }
536 /* Data will be save at the last reserve sector ,which is the front one of the fat base sector */
537 virSect = fs->fatbase - 1;
538
539 pdrv = LD2PD(vol); /* Driver index */
540
541 work = (BYTE *)ff_memalloc(SS(fs));
542 if (work == NULL) {
543 LEAVE_FF(fs, FR_NOT_ENOUGH_CORE);
544 }
545 /* Check and vertify partition information */
546 if (disk_read(pdrv, work, virSect, 1) != RES_OK) {
547 res = FR_DISK_ERR;
548 goto EXIT;
549 } /* Load VBR */
550
551 res = FatfsCheckStart(work, fs, vol);
552 if (res != FR_OK) {
553 goto EXIT;
554 }
555 /* Check the virtual parition amount if matched current setting or not */
556 fs->vir_amount = work[VR_PartitionCnt];
557 res = f_regvirfs(fs);
558 if (res != FR_OK) {
559 goto EXIT;
560 }
561
562 for (i = 0; i < _MAX_VIRVOLUMES; i++) {
563 if (i < work[VR_PartitionCnt]) {
564 if (work[VR_PARTITION + i * VR_ITEMSIZE + VR_Available] != 0x80) {
565 (void)f_unregvirfs(fs);
566 res = FR_MODIFIED;
567 goto EXIT;
568 }
569 } else {
570 if (work[VR_PARTITION + i * VR_ITEMSIZE + VR_Available] != 0x00) {
571 (void)f_unregvirfs(fs);
572 res = FR_MODIFIED;
573 goto EXIT;
574 }
575 break;
576 }
577
578 (void)memset_s(label, sizeof(label), 0, sizeof(label));
579
580 tmp = ld_dword(work + VR_PARTITION + i * VR_ITEMSIZE + VR_Entry + 0);
581 labelTmp = (DWORD *)label;
582 *labelTmp = tmp;
583 tmp = ld_dword(work + VR_PARTITION + i * VR_ITEMSIZE + VR_Entry + 4);
584 *((DWORD *)(label + 4)) = tmp;
585 tmp = ld_dword(work + VR_PARTITION + i * VR_ITEMSIZE + VR_Entry + 8);
586 *((DWORD *)(label + 8)) = tmp;
587 tmp = ld_dword(work + VR_PARTITION + i * VR_ITEMSIZE + VR_Entry + 12);
588 *((DWORD *)(label + 12)) = tmp;
589
590 if (f_checkname(label) != FR_OK) {
591 (void)f_unregvirfs(fs);
592 res = FR_MODIFIED;
593 goto EXIT;
594 }
595 (void)memcpy_s((CHILDFS(fs, i))->namelabel, _MAX_ENTRYLENGTH + 1, label, _MAX_ENTRYLENGTH + 1);
596
597 FatfsSetChildClst(work, fs, i);
598
599 /* External SD setting has overlimit the whole partition cluster amount */
600 if ((QWORD)(CHILDFS(fs, i))->st_clst + (QWORD)((CHILDFS(fs, i))->ct_clst) > (QWORD)fs->n_fatent) {
601 (void)f_unregvirfs(fs);
602 res = FR_MODIFIED;
603 goto EXIT;
604 }
605
606 res = FatfsCheckPartClst(fs, i);
607 if (res != FR_OK) {
608 goto EXIT;
609 }
610 if (i == (work[VR_PartitionCnt] - 1)) {
611 /*
612 * If the external SD virtual partition percent exceeds the error tolerance based on current virtual
613 * partition percent setting
614 */
615 res = FatfsCheckPercent(fs, i);
616 if (res != FR_OK) {
617 goto EXIT;
618 }
619 }
620 }
621 EXIT:
622 ff_memfree(work);
623 LEAVE_FF(fs, res);
624 }
625
FatfsClacPartInfo(FATFS * fs,DOUBLE virpartper,UINT i)626 static void FatfsClacPartInfo(FATFS *fs, DOUBLE virpartper, UINT i)
627 {
628 if (i == 0) {
629 (CHILDFS(fs, i))->st_clst = 3;
630 (CHILDFS(fs, i))->ct_clst = (DWORD)((fs->n_fatent - 3) *
631 g_fatVirPart.virtualinfo.virpartpercent[i]);
632
633 fs->st_clst = (CHILDFS(fs, i))->st_clst + (CHILDFS(fs, i))->ct_clst;
634 fs->ct_clst = fs->n_fatent - fs->st_clst;
635 } else if (i != (fs->vir_amount - 1)) {
636 (CHILDFS(fs, i))->st_clst = (CHILDFS(fs, (i - 1)))->st_clst + (CHILDFS(fs, (i - 1)))->ct_clst;
637 (CHILDFS(fs, i))->ct_clst = (DWORD)((fs->n_fatent - 3) *
638 g_fatVirPart.virtualinfo.virpartpercent[i]);
639 } else {
640 (CHILDFS(fs, i))->st_clst = (CHILDFS(fs, (i - 1)))->st_clst + (CHILDFS(fs, (i - 1)))->ct_clst;
641 if (virpartper <= (1 + _FLOAT_ACC) && virpartper >= (1 - _FLOAT_ACC)) {
642 (CHILDFS(fs, i))->ct_clst = fs->n_fatent - (CHILDFS(fs, i))->st_clst;
643 fs->st_clst = 0xFFFFFFFF;
644 fs->ct_clst = 0xFFFFFFFF;
645 } else {
646 (CHILDFS(fs, i))->ct_clst = (DWORD)((fs->n_fatent - 3) *
647 g_fatVirPart.virtualinfo.virpartpercent[i]);
648 fs->st_clst = (CHILDFS(fs, i))->st_clst + (CHILDFS(fs, i))->ct_clst;
649 fs->ct_clst = fs->n_fatent - fs->st_clst;
650 }
651 }
652 }
653
654 /*
655 * f_mkvtlpt:
656 * Apply the current virtual partition's setting to external SD card and to the CHILD FATFS
657 *
658 * Acceptable Return Value:
659 * - FR_OK : Successfully applied current setting to external SD card and
660 * CHILD FATFS
661 *
662 * Others Return Value :
663 * - FR_INVAILD_FATFS : The FATFS object has error or the info in it has been occuried
664 * - FR_DENIED : The virtual partition feature has been shut down by switcher
665 * - FR_INVALID_DRIVE : The drive index is error
666 * - FR_DISK_ERR : A Disk error happened
667 */
f_makevirpart(FATFS * fs,const TCHAR * path,BYTE vol)668 FRESULT f_makevirpart(FATFS *fs, const TCHAR *path, BYTE vol)
669 {
670 FRESULT res;
671 DWORD virSect;
672 DWORD startBaseSect, countBaseSect;
673 DWORD tmp;
674 CHAR label[_MAX_ENTRYLENGTH + 1];
675 DWORD *labelTmp = NULL; /* to clear the compilation warning */
676 UINT i;
677 BYTE pdrv;
678 BYTE *work = NULL;
679 DOUBLE virpartper = 0.0;
680
681 if (fs == NULL || (disk_status(fs->pdrv) & STA_NOINIT)) {
682 return FR_INVAILD_FATFS; /* The object is invalid */
683 }
684
685 /* Lock the filesystem object */
686 res = mount_volume(&path, &fs, FA_WRITE); /* Update the filesystem info to the parent fs */
687 if (res != FR_OK) {
688 LEAVE_FF(fs, res);
689 }
690
691 /* Only available in FAT32 filesystem */
692 if (ISCHILD(fs)) {
693 LEAVE_FF(fs, FR_INVAILD_FATFS);
694 }
695 /* Data will be save at the last reserve sector,which is the front one of the fat base sector */
696 virSect = fs->fatbase - 1;
697 /* Search the fs index, which same as the volume index */
698 pdrv = LD2PD(vol); /* Driver index */
699 countBaseSect = LD2PC(vol); /* Volume Base Sectors Count */
700 startBaseSect = LD2PS(vol); /* Volume Base Start Sector */
701
702 fs->vir_amount = g_fatVirPart.virtualinfo.virpartnum;
703 res = f_regvirfs(fs);
704 if (res != FR_OK) {
705 LEAVE_FF(fs, res);
706 }
707
708 work = (BYTE *)ff_memalloc(SS(fs));
709 if (work == NULL) {
710 LEAVE_FF(fs, FR_NOT_ENOUGH_CORE);
711 }
712 /* Data Cluster is begin from the Cluster #3 to the last cluster */
713 /* Cluster #0 #1 is for VBR, reserve sectors and fat */
714 /* Cluster #2 is for root directory */
715 (void)memset_s(work, SS(fs), 0, SS(fs));
716
717 for (i = 0; i < fs->vir_amount; i++) {
718 /* Copy the Entry label and write to work sector's buffer */
719 (void)memset_s(label, sizeof(label), 0, sizeof(label));
720 (void)memcpy_s(label, _MAX_ENTRYLENGTH + 1, g_fatVirPart.virtualinfo.virpartname[i], _MAX_ENTRYLENGTH + 1);
721 labelTmp = (DWORD *)label;
722 tmp = *labelTmp;
723 st_dword(work + VR_PARTITION + i * VR_ITEMSIZE + VR_Entry + 0, tmp);
724 tmp = *((DWORD *)(label + 4));
725 st_dword(work + VR_PARTITION + i * VR_ITEMSIZE + VR_Entry + 4, tmp);
726 tmp = *((DWORD *)(label + 8));
727 st_dword(work + VR_PARTITION + i * VR_ITEMSIZE + VR_Entry + 8, tmp);
728 tmp = *((DWORD *)(label + 12));
729 st_dword(work + VR_PARTITION + i * VR_ITEMSIZE + VR_Entry + 12, tmp);
730
731 virpartper += g_fatVirPart.virtualinfo.virpartpercent[i];
732
733 (void)memcpy_s((CHILDFS(fs, i))->namelabel, _MAX_ENTRYLENGTH + 1, g_fatVirPart.virtualinfo.virpartname[i],
734 _MAX_ENTRYLENGTH + 1);
735 FatfsClacPartInfo(fs, virpartper, i);
736 (CHILDFS(fs, i))->last_clst = (CHILDFS(fs, i))->st_clst - 1;
737 work[VR_PARTITION + i * VR_ITEMSIZE + VR_Available] = 0x80;
738 }
739
740 /* Set the data to sector */
741 work[VR_PartitionCnt] = fs->vir_amount;
742 work[VR_PartitionFSType] = fs->fs_type;
743 st_dword(work + VR_PartitionStSec, startBaseSect);
744 st_dword(work + VR_PartitionCtSec, countBaseSect);
745 st_word(work + VR_PartitionClstSz, fs->csize);
746 st_dword(work + VR_PartitionCtClst, fs->n_fatent);
747 for (i = 0; i < fs->vir_amount; i++) {
748 st_dword(work + VR_PARTITION + i * VR_ITEMSIZE + VR_StartClust,
749 (CHILDFS(fs, i))->st_clst);
750 st_dword(work + VR_PARTITION + i * VR_ITEMSIZE + VR_CountClust,
751 (CHILDFS(fs, i))->ct_clst);
752 }
753
754 /* ASCII for Keyword "LITE" */
755 st_dword(work + VR_VertifyString, 0x4C495445);
756
757 /* Write into the data area */
758 if (disk_write(pdrv, work, virSect, 1) != RES_OK) {
759 (void)f_unregvirfs(fs);
760 res = FR_DISK_ERR;
761 }
762
763 ff_memfree(work);
764 LEAVE_FF(fs, res);
765 }
766
f_getvirfree(const TCHAR * path,DWORD * nclst,DWORD * cclst)767 FRESULT f_getvirfree(const TCHAR *path, DWORD *nclst, DWORD *cclst)
768 {
769 FATFS *fs = NULL;
770 FRESULT res;
771 DWORD clst, link;
772 DWORD nfree;
773 UINT i;
774 DIR dj;
775
776 /* Find volume to Update the global FSINFO */
777 res = mount_volume(&path, &fs, 0);
778 if (res != FR_OK) {
779 LEAVE_FF(fs, res);
780 }
781
782 /* Following the entry keyword, decide to replace the PARENT FATFS to CHILD FATFS or not */
783 dj.obj.fs = fs;
784 if (ISVIRPART(fs)) {
785 /* Check the virtual partition top directory, and match the virtual fs */
786 res = follow_virentry(&dj.obj, path);
787 if (res == FR_INT_ERR) {
788 LEAVE_FF(fs, res);
789 }
790 if (res == FR_OK) {
791 fs = dj.obj.fs;
792 }
793 } else {
794 /* Virtual Partition Feature was off, deny this operation */
795 LEAVE_FF(fs, FR_DENIED);
796 }
797
798 /* If current FATFS is a CHILD FATFS */
799 if (ISCHILD(fs)) {
800 /* If CHILD FATFS' free_clst is invalid, the scan the FAT and update it */
801 if (fs->free_clst > fs->ct_clst) {
802 dj.obj.fs = fs;
803 fs->free_clst = fs->ct_clst;
804 for (clst = fs->st_clst; clst < fs->st_clst + fs->ct_clst; clst++) {
805 link = get_fat(&dj.obj, clst);
806 if (link == 0) {
807 continue;
808 }
809 fs->free_clst--;
810 }
811 }
812 *nclst = fs->free_clst;
813 *cclst = fs->ct_clst;
814 LEAVE_FF(fs, FR_OK);
815 } else {
816 nfree = 0;
817 if (fs->ct_clst == 0xFFFFFFFF) {
818 LEAVE_FF(fs, FR_DENIED);
819 }
820 for (i = 0; i < fs->vir_amount; i++) {
821 if (CHILDFS(fs, i)->free_clst > CHILDFS(fs, i)->ct_clst) {
822 dj.obj.fs = CHILDFS(fs, i);
823 CHILDFS(fs, i)->free_clst = CHILDFS(fs, i)->ct_clst;
824 for (clst = CHILDFS(fs, i)->st_clst; clst < CHILDFS(fs, i)->st_clst + CHILDFS(fs, i)->ct_clst; clst++) {
825 link = get_fat(&dj.obj, clst);
826 if (link == 0) {
827 continue;
828 }
829 CHILDFS(fs, i)->free_clst--;
830 }
831 }
832 nfree += CHILDFS(fs, i)->free_clst;
833 }
834 *nclst = fs->free_clst - nfree;
835 *cclst = fs->ct_clst;
836 LEAVE_FF(fs, FR_OK);
837 }
838 }
839 #endif
840