• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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