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 "virpart.h"
33 #include "errno.h"
34 #include "fatfs.h"
35 #include "errcode_fat.h"
36 #include "disk.h"
37 #include "fs/mount.h"
38
39 #ifdef LOSCFG_FS_FAT_CACHE
40 #include "bcache.h"
41 #endif
42
43 #include "virpartff.h"
44
45 #ifdef LOSCFG_FS_FAT_VIRTUAL_PARTITION
46
47 char g_devPartName[DISK_NAME + 1] = {0};
48
FatFsCheckPercent(virpartinfo * virtualinfo)49 static INT FatFsCheckPercent(virpartinfo *virtualinfo)
50 {
51 double virtPercent = 0.0;
52 INT partCount;
53
54 for (partCount = 0; partCount < virtualinfo->virpartnum; partCount++) {
55 if (virtualinfo->virpartpercent[partCount] <= _FLOAT_ACC ||
56 virtualinfo->virpartpercent[partCount] >= (1 - _FLOAT_ACC)) {
57 set_errno(VIRERR_PARMPERCENTERR);
58 g_fatVirPart.isparamset = FALSE;
59 return -1;
60 }
61 virtPercent += virtualinfo->virpartpercent[partCount];
62 }
63
64 if (virtPercent >= (1 + _FLOAT_ACC)) {
65 set_errno(VIRERR_PARMPERCENTERR);
66 g_fatVirPart.isparamset = FALSE;
67 return -1;
68 }
69
70 return 0;
71 }
72
FatFsCheckName(virpartinfo * virtualinfo)73 static INT FatFsCheckName(virpartinfo *virtualinfo)
74 {
75 INT partCount, loopCount, len;
76 FRESULT res;
77
78 for (partCount = 0; partCount < virtualinfo->virpartnum; partCount++) {
79 len = ff_strnlen(virtualinfo->virpartname[partCount], _MAX_ENTRYLENGTH + 1);
80 if (len <= 0 || len > _MAX_ENTRYLENGTH) {
81 set_errno(VIRERR_PARMNAMEERR);
82 g_fatVirPart.isparamset = FALSE;
83 return -1;
84 }
85
86 res = f_checkname(virtualinfo->virpartname[partCount]);
87 if (res != FR_OK) {
88 set_errno(VIRERR_PARMNAMEERR);
89 g_fatVirPart.isparamset = FALSE;
90 return -1;
91 }
92 }
93
94 for (partCount = 0; partCount < virtualinfo->virpartnum; partCount++) {
95 for (loopCount = partCount + 1; loopCount < virtualinfo->virpartnum; loopCount++) {
96 if ((memcmp(virtualinfo->virpartname[partCount], virtualinfo->virpartname[loopCount],
97 _MAX_ENTRYLENGTH)) == 0) {
98 set_errno(VIRERR_PARMNAMEERR);
99 g_fatVirPart.isparamset = FALSE;
100 return -1;
101 }
102 }
103 }
104
105 return 0;
106 }
107
los_set_virpartparam(virpartinfo virtualinfo)108 INT los_set_virpartparam(virpartinfo virtualinfo)
109 {
110 INT ret;
111 UINT minSize;
112 UINT dstNameSize;
113 char devHead[DISK_NAME + 1] = "/dev/";
114
115 g_fatVirPart.virtualinfo.devpartpath = g_devPartName;
116
117 if (g_fatVirPart.isparamset == TRUE) {
118 set_errno(VIRERR_PARMLOCKED);
119 return -1;
120 }
121
122 g_fatVirPart.isparamset = TRUE;
123
124 if (virtualinfo.virpartnum < 1 || virtualinfo.virpartnum > _MAX_VIRVOLUMES) {
125 set_errno(VIRERR_PARMNUMERR);
126 g_fatVirPart.isparamset = FALSE;
127 return -1;
128 }
129
130 ret = FatFsCheckPercent(&virtualinfo);
131 if (ret != 0) {
132 return ret;
133 }
134
135 ret = FatFsCheckName(&virtualinfo);
136 if (ret != 0) {
137 return ret;
138 }
139
140 if (strlen(virtualinfo.devpartpath) <= strlen(devHead)) {
141 set_errno(VIRERR_PARMDEVERR);
142 g_fatVirPart.isparamset = FALSE;
143 return -1;
144 }
145
146 if (memcmp(virtualinfo.devpartpath, devHead, strlen(devHead)) != 0) {
147 set_errno(VIRERR_PARMDEVERR);
148 g_fatVirPart.isparamset = FALSE;
149 return -1;
150 }
151
152 dstNameSize = sizeof(g_devPartName);
153 minSize = dstNameSize < strlen(virtualinfo.devpartpath) ? dstNameSize : strlen(virtualinfo.devpartpath);
154 ret = strncpy_s(g_fatVirPart.virtualinfo.devpartpath, dstNameSize, virtualinfo.devpartpath, minSize);
155 if (ret != EOK) {
156 set_errno(VIRERR_PARMNAMEERR);
157 g_fatVirPart.isparamset = FALSE;
158 return -1;
159 }
160 g_fatVirPart.virtualinfo.devpartpath[dstNameSize - 1] = '\0';
161
162 (void)memcpy_s(g_fatVirPart.virtualinfo.virpartname, sizeof(g_fatVirPart.virtualinfo.virpartname),
163 virtualinfo.virpartname, sizeof(virtualinfo.virpartname));
164 (void)memcpy_s(g_fatVirPart.virtualinfo.virpartpercent, sizeof(g_fatVirPart.virtualinfo.virpartpercent),
165 virtualinfo.virpartpercent, sizeof(virtualinfo.virpartpercent));
166 g_fatVirPart.virtualinfo.virpartnum = virtualinfo.virpartnum;
167
168 return 0;
169 }
170
FatfsDisablePart(void * handle)171 static INT FatfsDisablePart(void *handle)
172 {
173 FATFS *fs = (FATFS *)handle;
174 return fatfs_2_vfs(f_disvirfs(fs));
175 }
176
177 /*
178 * FatfsScanFat:
179 * Scan the FAT inside the boundary of CHILD FATFS limit, and update the free cluster and last cluster
180 * for all CHILD FATFS.
181 * Acceptable Return Value:
182 * - FR_OK : Successfully scanned the FAT and update field.
183 * Others Return Value:
184 * - FR_INVAILD_FATFS : The FATFS object has error or the info in it has been occuried
185 * - FR_DENIED : The virtual partition feature has been shut down by switcher
186 * - FR_DISK_ERR : A disk error happened
187 */
FatfsScanFat(void * handle)188 static INT FatfsScanFat(void *handle)
189 {
190 FATFS *fat = (FATFS *)handle;
191 UINT i;
192 INT ret = FR_OK;
193
194 for (i = 0; i < fat->vir_amount; i++) {
195 /* Assert error will not abort the scanning process */
196 ret = f_scanfat(CHILDFS(fat, i));
197 if (ret != FR_OK) {
198 break;
199 }
200 }
201
202 return fatfs_2_vfs(ret);
203 }
204
205 /*
206 * FatfsScanClear:
207 * Scan the root directory clean completely, regardless of the virtual partition entry.
208 * Acceptable Return Value:
209 * - FR_OK : The root directory is completely clean.
210 * - FR_NOTCLEAR : The root dircotory is not clean.
211 * Others Return Value:
212 * Followed the by the lower API.
213 */
FatfsScanClear(INT vol)214 static FRESULT FatfsScanClear(INT vol)
215 {
216 FRESULT ret;
217 DIR dir;
218 FILINFO fno;
219 CHAR path[MAX_LFNAME_LENGTH];
220 INT num;
221 INT res;
222
223 /* num : for the amount of all item in root directory */
224 num = 0;
225
226 (void)memset_s(path, sizeof(path), 0, sizeof(path));
227
228 res = snprintf_s(path, MAX_LFNAME_LENGTH, MAX_LFNAME_LENGTH - 1, "%d:/", vol);
229 if (res < 0) {
230 return FR_INVALID_NAME;
231 }
232
233 ret = f_opendir(&dir, path);
234 if (ret != FR_OK) {
235 return ret;
236 }
237
238 /* Scan all entry for searching virtual partition directory and others in root directory */
239 while (1) {
240 (void)memset_s(&fno, sizeof(FILINFO), 0, sizeof(FILINFO));
241 ret = f_readdir(&dir, &fno);
242 /* Reach the end of directory, or the root direcotry is empty, abort the scanning operation */
243 if (fno.fname[0] == 0x00 || fno.fname[0] == (TCHAR)0xFF) {
244 break;
245 }
246
247 if (ret != FR_OK) {
248 (void)f_closedir(&dir);
249 return ret;
250 }
251 num++;
252 }
253
254 /* Close the directory */
255 ret = f_closedir(&dir);
256 if ((ret == FR_OK) && (num != 0)) {
257 return FR_NOTCLEAR;
258 }
259
260 return ret;
261 }
262
263 /*
264 * FatfsBuildEntry:
265 * Scan virtual partition entry in root directory, and try to rebuild the
266 * error virtual partition, according to the scanning result.
267 * Acceptable Return Value:
268 * - FR_OK : The root directory is completely clean.
269 * - FR_OCCUPIED : The virtual partition entry has been occupied by the same name file.
270 * - FR_CHAIN_ERR : The virtual partition entry has been rebuilt along the invalid cluster
271 * chain.
272 * Others Return Value:
273 * Followed the by the lower API
274 */
FatfsBuildEntry(FATFS * fat,INT vol)275 static FRESULT FatfsBuildEntry(FATFS *fat, INT vol)
276 {
277 UINT i;
278 CHAR path[MAX_LFNAME_LENGTH];
279 FRESULT ret;
280 DIR dir;
281 INT res;
282
283 for (i = 0; i < fat->vir_amount; i++) {
284 res = snprintf_s(path, MAX_LFNAME_LENGTH, MAX_LFNAME_LENGTH - 1, "%d:%s", vol, CHILDFS(fat, i)->namelabel);
285 if (res < 0) {
286 return FR_INVALID_NAME;
287 }
288
289 ret = f_mkdir(path);
290 if (ret == FR_EXIST) {
291 (void)memset_s(&dir, sizeof(dir), 0, sizeof(dir));
292 ret = f_opendir(&dir, path);
293 if (ret == FR_NO_DIR) {
294 return FR_OCCUPIED;
295 }
296 if (ret == FR_OK) {
297 ret = f_boundary(CHILDFS(fat, i), dir.obj.sclust);
298 if (ret != FR_OK) {
299 (void)f_closedir(&dir);
300 return ret;
301 }
302 } else {
303 return ret;
304 }
305 ret = f_closedir(&dir);
306 if (ret != FR_OK) {
307 return ret;
308 }
309 } else if (ret != FR_OK) {
310 return ret;
311 }
312 }
313
314 return FR_OK;
315 }
316
317 /*
318 * FatFsUnbindVirPart:
319 * Uninitialized the CHILD FATFS object
320 *
321 * Acceptable Return Value:
322 * - ENOERR : Successfully initialized the CHILD FATFS object.
323 */
FatFsUnbindVirPart(void * handle)324 INT FatFsUnbindVirPart(void *handle)
325 {
326 INT ret;
327 FATFS *fat = (FATFS *)handle;
328 ret = f_unregvirfs(fat);
329 return fatfs_2_vfs(ret);
330 }
331
332 /*
333 * FatFsBindVirPart:
334 * Bind the virtual partition
335 *
336 * Acceptable Return Value:
337 * - FR_OK : Finished the virtual partition.
338 * The virtual partition result can refer by API
339 */
FatFsBindVirPart(void * handle,BYTE vol)340 INT FatFsBindVirPart(void *handle, BYTE vol)
341 {
342 INT ret;
343 FATFS *fat = (FATFS *)handle;
344 char path[MAX_LFNAME_LENGTH] = {0};
345
346 if (fat == NULL) {
347 return -EINVAL;
348 }
349
350 ret = snprintf_s(path, sizeof(path), sizeof(path) - 1, "%u:/", vol);
351 if (ret < 0) {
352 return -EINVAL;
353 }
354
355 /* Detect the information in the reserve sector and try to set virtual partition by sector info */
356 ret = f_checkvirpart(fat, path, vol);
357 if (ret == FR_NOVIRPART) {
358 /* Failed to use the External SD setting, try to use the current setting to external SD and rebuild entries */
359 /* Check the enviorment whether it is fit for virtual parition */
360 ret = FatfsScanClear(vol);
361 if (ret != FR_OK) {
362 /* A error happened in the file operation */
363 (void)FatfsDisablePart(fat);
364 return fatfs_2_vfs(ret);
365 }
366 /* Ready to fit the SD card to virtual partition featrue */
367 ret = f_makevirpart(fat, path, vol);
368 if (ret != FR_OK) {
369 (void)FatfsDisablePart(fat);
370 return fatfs_2_vfs(ret);
371 }
372 } else if (ret != FR_OK) {
373 return fatfs_2_vfs(ret);
374 }
375 /* Try to build the virtual entry */
376 ret = FatfsBuildEntry(fat, vol);
377 if (ret != FR_OK) {
378 (void)FatfsDisablePart(fat);
379 return fatfs_2_vfs(ret);
380 }
381 #ifdef LOSCFG_FS_FAT_CACHE
382 los_part *part = NULL;
383 if (ret == FR_OK) {
384 part = get_part((int)fat->pdrv);
385 if (part != NULL) {
386 ret = OsSdSync(part->disk_id);
387 if (ret < 0) {
388 ret = -EIO;
389 }
390 } else {
391 return -ENODEV;
392 }
393 }
394 #endif
395 if (ret == FR_OK) {
396 ret = FatfsScanFat(fat);
397 if (ret != FR_OK) {
398 (void)FatfsDisablePart(fat);
399 return fatfs_2_vfs(ret);
400 }
401 }
402 return fatfs_2_vfs(ret);
403 }
404
405 /*
406 * FatFsMakeVirPart:
407 * Bind the virtual partition
408 *
409 * Acceptable Return Value:
410 * - FR_OK : Finished the virtual partition.
411 * The virtual partition result can refer by API
412 */
FatFsMakeVirPart(void * handle,BYTE vol)413 INT FatFsMakeVirPart(void *handle, BYTE vol)
414 {
415 INT ret;
416 FATFS *fat = (FATFS *)handle;
417 char path[MAX_LFNAME_LENGTH] = {0};
418
419 if (fat == NULL) {
420 return -EINVAL;
421 }
422
423 ret = snprintf_s(path, sizeof(path), sizeof(path) - 1, "%u:/", vol);
424 if (ret < 0) {
425 return -EINVAL;
426 }
427
428 /* Ready to fit the SD card to virtual partition featrue */
429 ret = f_makevirpart(fat, path, vol);
430 if (ret != FR_OK) {
431 (void)FatfsDisablePart(fat);
432 return fatfs_2_vfs(ret);
433 }
434
435 /* Try to build the virtual entry */
436 ret = FatfsBuildEntry(fat, vol);
437 if (ret != FR_OK) {
438 (void)FatfsDisablePart(fat);
439 return fatfs_2_vfs(ret);
440 }
441
442 return fatfs_2_vfs(ret);
443 }
444
fatfs_virstatfs_internel(struct Vnode * mountpt,const char * relpath,struct statfs * buf)445 INT fatfs_virstatfs_internel(struct Vnode *mountpt, const char *relpath, struct statfs *buf)
446 {
447 char drive[MAX_LFNAME_LENGTH];
448 DWORD freClust, allClust;
449 FATFS *fat = NULL;
450 INT result, vol;
451
452 fat = (FATFS *)(mountpt->originMount->data);
453 if (fat == NULL) {
454 return -EINVAL;
455 }
456
457 if (fat->vir_flag != FS_PARENT) {
458 return -EINVAL;
459 }
460
461 vol = fatfs_get_vol(fat);
462 if (vol < 0 || vol > FF_VOLUMES) {
463 return -ENOENT;
464 }
465
466 if (strlen(relpath) > MAX_LFNAME_LENGTH) {
467 return -EFAULT;
468 }
469
470 result = snprintf_s(drive, sizeof(drive), sizeof(drive) - 1, "%d:%s", vol, relpath);
471 if (result < 0) {
472 return -EINVAL;
473 }
474
475 result = f_getvirfree(drive, &freClust, &allClust);
476 if (result != FR_OK) {
477 result = fatfs_2_vfs(result);
478 goto EXIT;
479 }
480
481 (void)memset_s((void *)buf, sizeof(struct statfs), 0, sizeof(struct statfs));
482 buf->f_type = MSDOS_SUPER_MAGIC;
483 buf->f_bfree = freClust;
484 buf->f_bavail = freClust;
485 buf->f_blocks = allClust;
486 #if FF_MAX_SS != FF_MIN_SS
487 buf->f_bsize = fat->ssize * fat->csize;
488 #else
489 buf->f_bsize = FF_MIN_SS * fat->csize;
490 #endif
491 #if FF_USE_LFN
492 buf->f_namelen = FF_MAX_LFN; /* Maximum length of filenames */
493 #else
494 /*
495 * Maximum length of filenames,'8' is the effective length, '1' is the terminator,
496 * and '3' is the special length
497 */
498 buf->f_namelen = (8 + 1 + 3);
499 #endif
500
501 EXIT:
502 return result;
503 }
504
505 #endif
506