1 /* 2 * Copyright (c) 2006-2018, RT-Thread Development Team 3 * 4 * SPDX-License-Identifier: Apache-2.0 5 * 6 * Change Logs: 7 * Date Author Notes 8 * 2005-02-22 Bernard The first version. 9 * 2010-06-30 Bernard Optimize for RT-Thread RTOS 10 * 2011-03-12 Bernard fix the filesystem lookup issue. 11 * 2017-11-30 Bernard fix the filesystem_operation_table issue. 12 * 2017-12-05 Bernard fix the fs type search issue in mkfs. 13 */ 14 15 #include <dfs_fs.h> 16 #include <dfs_file.h> 17 #include "dfs_private.h" 18 19 /** 20 * @addtogroup FsApi 21 */ 22 /*@{*/ 23 24 /** 25 * this function will register a file system instance to device file system. 26 * 27 * @param ops the file system instance to be registered. 28 * 29 * @return 0 on successful, -1 on failed. 30 */ 31 int dfs_register(const struct dfs_filesystem_ops *ops) 32 { 33 int ret = RT_EOK; 34 const struct dfs_filesystem_ops **empty = NULL; 35 const struct dfs_filesystem_ops **iter; 36 37 /* lock filesystem */ 38 dfs_lock(); 39 /* check if this filesystem was already registered */ 40 for (iter = &filesystem_operation_table[0]; 41 iter < &filesystem_operation_table[DFS_FILESYSTEM_TYPES_MAX]; iter ++) 42 { 43 /* find out an empty filesystem type entry */ 44 if (*iter == NULL) 45 (empty == NULL) ? (empty = iter) : 0; 46 else if (strcmp((*iter)->name, ops->name) == 0) 47 { 48 rt_set_errno(-EEXIST); 49 ret = -1; 50 break; 51 } 52 } 53 54 /* save the filesystem's operations */ 55 if (empty == NULL) 56 { 57 rt_set_errno(-ENOSPC); 58 LOG_E("There is no space to register this file system (%d).", ops->name); 59 ret = -1; 60 } 61 else if (ret == RT_EOK) 62 { 63 *empty = ops; 64 } 65 66 dfs_unlock(); 67 return ret; 68 } 69 70 /** 71 * this function will return the file system mounted on specified path. 72 * 73 * @param path the specified path string. 74 * 75 * @return the found file system or NULL if no file system mounted on 76 * specified path 77 */ 78 struct dfs_filesystem *dfs_filesystem_lookup(const char *path) 79 { 80 struct dfs_filesystem *iter; 81 struct dfs_filesystem *fs = NULL; 82 uint32_t fspath, prefixlen; 83 84 prefixlen = 0; 85 86 RT_ASSERT(path); 87 88 /* lock filesystem */ 89 dfs_lock(); 90 91 /* lookup it in the filesystem table */ 92 for (iter = &filesystem_table[0]; 93 iter < &filesystem_table[DFS_FILESYSTEMS_MAX]; iter++) 94 { 95 if ((iter->path == NULL) || (iter->ops == NULL)) 96 continue; 97 98 fspath = strlen(iter->path); 99 if ((fspath < prefixlen) 100 || (strncmp(iter->path, path, fspath) != 0)) 101 continue; 102 103 /* check next path separator */ 104 if (fspath > 1 && (strlen(path) > fspath) && (path[fspath] != '/')) 105 continue; 106 107 fs = iter; 108 prefixlen = fspath; 109 } 110 111 dfs_unlock(); 112 113 return fs; 114 } 115 116 /** 117 * this function will return the mounted path for specified device. 118 * 119 * @param device the device object which is mounted. 120 * 121 * @return the mounted path or NULL if none device mounted. 122 */ 123 const char* dfs_filesystem_get_mounted_path(struct rt_device* device) 124 { 125 const char* path = NULL; 126 struct dfs_filesystem *iter; 127 128 dfs_lock(); 129 for (iter = &filesystem_table[0]; 130 iter < &filesystem_table[DFS_FILESYSTEMS_MAX]; iter++) 131 { 132 /* fint the mounted device */ 133 if (iter->ops == NULL) continue; 134 else if (iter->dev_id == device) 135 { 136 path = iter->path; 137 break; 138 } 139 } 140 141 /* release filesystem_table lock */ 142 dfs_unlock(); 143 144 return path; 145 } 146 147 /** 148 * this function will fetch the partition table on specified buffer. 149 * 150 * @param part the returned partition structure. 151 * @param buf the buffer contains partition table. 152 * @param pindex the index of partition table to fetch. 153 * 154 * @return RT_EOK on successful or -RT_ERROR on failed. 155 */ 156 int dfs_filesystem_get_partition(struct dfs_partition *part, 157 uint8_t *buf, 158 uint32_t pindex) 159 { 160 #define DPT_ADDRESS 0x1be /* device partition offset in Boot Sector */ 161 #define DPT_ITEM_SIZE 16 /* partition item size */ 162 163 uint8_t *dpt; 164 uint8_t type; 165 166 RT_ASSERT(part != NULL); 167 RT_ASSERT(buf != NULL); 168 169 dpt = buf + DPT_ADDRESS + pindex * DPT_ITEM_SIZE; 170 171 /* check if it is a valid partition table */ 172 if ((*dpt != 0x80) && (*dpt != 0x00)) 173 return -EIO; 174 175 /* get partition type */ 176 type = *(dpt+4); 177 if (type == 0) 178 return -EIO; 179 180 /* set partition information 181 * size is the number of 512-Byte */ 182 part->type = type; 183 part->offset = *(dpt+8) | *(dpt+9)<<8 | *(dpt+10)<<16 | *(dpt+11)<<24; 184 part->size = *(dpt+12) | *(dpt+13)<<8 | *(dpt+14)<<16 | *(dpt+15)<<24; 185 186 rt_kprintf("found part[%d], begin: %d, size: ", 187 pindex, part->offset*512); 188 if ((part->size>>11) == 0) 189 rt_kprintf("%d%s",part->size>>1,"KB\n"); /* KB */ 190 else 191 { 192 unsigned int part_size; 193 part_size = part->size >> 11; /* MB */ 194 if ((part_size>>10) == 0) 195 rt_kprintf("%d.%d%s",part_size,(part->size>>1)&0x3FF,"MB\n"); 196 else 197 rt_kprintf("%d.%d%s",part_size>>10,part_size&0x3FF,"GB\n"); 198 } 199 200 return RT_EOK; 201 } 202 203 /** 204 * this function will mount a file system on a specified path. 205 * 206 * @param device_name the name of device which includes a file system. 207 * @param path the path to mount a file system 208 * @param filesystemtype the file system type 209 * @param rwflag the read/write etc. flag. 210 * @param data the private data(parameter) for this file system. 211 * 212 * @return 0 on successful or -1 on failed. 213 */ 214 int dfs_mount(const char *device_name, 215 const char *path, 216 const char *filesystemtype, 217 unsigned long rwflag, 218 const void *data) 219 { 220 const struct dfs_filesystem_ops **ops; 221 struct dfs_filesystem *iter; 222 struct dfs_filesystem *fs = NULL; 223 char *fullpath = NULL; 224 rt_device_t dev_id; 225 226 /* open specific device */ 227 if (device_name == NULL) 228 { 229 /* which is a non-device filesystem mount */ 230 dev_id = NULL; 231 } 232 else if ((dev_id = rt_device_find(device_name)) == NULL) 233 { 234 /* no this device */ 235 rt_set_errno(-ENODEV); 236 return -1; 237 } 238 239 /* find out the specific filesystem */ 240 dfs_lock(); 241 242 for (ops = &filesystem_operation_table[0]; 243 ops < &filesystem_operation_table[DFS_FILESYSTEM_TYPES_MAX]; ops++) 244 if ((*ops != NULL) && (strcmp((*ops)->name, filesystemtype) == 0)) 245 break; 246 247 dfs_unlock(); 248 249 if (ops == &filesystem_operation_table[DFS_FILESYSTEM_TYPES_MAX]) 250 { 251 /* can't find filesystem */ 252 rt_set_errno(-ENODEV); 253 return -1; 254 } 255 256 /* check if there is mount implementation */ 257 if ((*ops == NULL) || ((*ops)->mount == NULL)) 258 { 259 rt_set_errno(-ENOSYS); 260 return -1; 261 } 262 263 /* make full path for special file */ 264 fullpath = dfs_normalize_path(NULL, path); 265 if (fullpath == NULL) /* not an abstract path */ 266 { 267 rt_set_errno(-ENOTDIR); 268 return -1; 269 } 270 271 /* Check if the path exists or not, raw APIs call, fixme */ 272 if ((strcmp(fullpath, "/") != 0) && (strcmp(fullpath, "/dev") != 0)) 273 { 274 struct dfs_fd fd; 275 276 if (dfs_file_open(&fd, fullpath, O_RDONLY | O_DIRECTORY) < 0) 277 { 278 rt_free(fullpath); 279 rt_set_errno(-ENOTDIR); 280 281 return -1; 282 } 283 dfs_file_close(&fd); 284 } 285 286 /* check whether the file system mounted or not in the filesystem table 287 * if it is unmounted yet, find out an empty entry */ 288 dfs_lock(); 289 290 for (iter = &filesystem_table[0]; 291 iter < &filesystem_table[DFS_FILESYSTEMS_MAX]; iter++) 292 { 293 /* check if it is an empty filesystem table entry? if it is, save fs */ 294 if (iter->ops == NULL) 295 (fs == NULL) ? (fs = iter) : 0; 296 /* check if the PATH is mounted */ 297 else if (strcmp(iter->path, path) == 0) 298 { 299 rt_set_errno(-EINVAL); 300 goto err1; 301 } 302 } 303 304 if ((fs == NULL) && (iter == &filesystem_table[DFS_FILESYSTEMS_MAX])) 305 { 306 rt_set_errno(-ENOSPC); 307 LOG_E("There is no space to mount this file system (%s).", filesystemtype); 308 goto err1; 309 } 310 311 /* register file system */ 312 fs->path = fullpath; 313 fs->ops = *ops; 314 fs->dev_id = dev_id; 315 /* release filesystem_table lock */ 316 dfs_unlock(); 317 318 /* open device, but do not check the status of device */ 319 if (dev_id != NULL) 320 { 321 if (rt_device_open(fs->dev_id, 322 RT_DEVICE_OFLAG_RDWR) != RT_EOK) 323 { 324 /* The underlaying device has error, clear the entry. */ 325 dfs_lock(); 326 memset(fs, 0, sizeof(struct dfs_filesystem)); 327 328 goto err1; 329 } 330 } 331 332 /* call mount of this filesystem */ 333 if ((*ops)->mount(fs, rwflag, data) < 0) 334 { 335 /* close device */ 336 if (dev_id != NULL) 337 rt_device_close(fs->dev_id); 338 339 /* mount failed */ 340 dfs_lock(); 341 /* clear filesystem table entry */ 342 memset(fs, 0, sizeof(struct dfs_filesystem)); 343 344 goto err1; 345 } 346 347 return 0; 348 349 err1: 350 dfs_unlock(); 351 rt_free(fullpath); 352 353 return -1; 354 } 355 356 /** 357 * this function will unmount a file system on specified path. 358 * 359 * @param specialfile the specified path which mounted a file system. 360 * 361 * @return 0 on successful or -1 on failed. 362 */ 363 int dfs_unmount(const char *specialfile) 364 { 365 char *fullpath; 366 struct dfs_filesystem *iter; 367 struct dfs_filesystem *fs = NULL; 368 369 fullpath = dfs_normalize_path(NULL, specialfile); 370 if (fullpath == NULL) 371 { 372 rt_set_errno(-ENOTDIR); 373 374 return -1; 375 } 376 377 /* lock filesystem */ 378 dfs_lock(); 379 380 for (iter = &filesystem_table[0]; 381 iter < &filesystem_table[DFS_FILESYSTEMS_MAX]; iter++) 382 { 383 /* check if the PATH is mounted */ 384 if ((iter->path != NULL) && (strcmp(iter->path, fullpath) == 0)) 385 { 386 fs = iter; 387 break; 388 } 389 } 390 391 if (fs == NULL || 392 fs->ops->unmount == NULL || 393 fs->ops->unmount(fs) < 0) 394 { 395 goto err1; 396 } 397 398 /* close device, but do not check the status of device */ 399 if (fs->dev_id != NULL) 400 rt_device_close(fs->dev_id); 401 402 if (fs->path != NULL) 403 rt_free(fs->path); 404 405 /* clear this filesystem table entry */ 406 memset(fs, 0, sizeof(struct dfs_filesystem)); 407 408 dfs_unlock(); 409 rt_free(fullpath); 410 411 return 0; 412 413 err1: 414 dfs_unlock(); 415 rt_free(fullpath); 416 417 return -1; 418 } 419 420 /** 421 * make a file system on the special device 422 * 423 * @param fs_name the file system name 424 * @param device_name the special device name 425 * 426 * @return 0 on successful, otherwise failed. 427 */ 428 int dfs_mkfs(const char *fs_name, const char *device_name) 429 { 430 int index; 431 rt_device_t dev_id = NULL; 432 433 /* check device name, and it should not be NULL */ 434 if (device_name != NULL) 435 dev_id = rt_device_find(device_name); 436 437 if (dev_id == NULL) 438 { 439 rt_set_errno(-ENODEV); 440 LOG_E("Device (%s) was not found", device_name); 441 return -1; 442 } 443 444 /* lock file system */ 445 dfs_lock(); 446 /* find the file system operations */ 447 for (index = 0; index <= DFS_FILESYSTEM_TYPES_MAX; index ++) 448 { 449 if (filesystem_operation_table[index] != NULL && 450 strcmp(filesystem_operation_table[index]->name, fs_name) == 0) 451 break; 452 } 453 dfs_unlock(); 454 455 if (index <= DFS_FILESYSTEM_TYPES_MAX) 456 { 457 /* find file system operation */ 458 const struct dfs_filesystem_ops *ops = filesystem_operation_table[index]; 459 if (ops->mkfs == NULL) 460 { 461 LOG_E("The file system (%s) mkfs function was not implement", fs_name); 462 rt_set_errno(-ENOSYS); 463 return -1; 464 } 465 466 return ops->mkfs(dev_id); 467 } 468 469 LOG_E("File system (%s) was not found.", fs_name); 470 471 return -1; 472 } 473 474 /** 475 * this function will return the information about a mounted file system. 476 * 477 * @param path the path which mounted file system. 478 * @param buffer the buffer to save the returned information. 479 * 480 * @return 0 on successful, others on failed. 481 */ 482 int dfs_statfs(const char *path, struct statfs *buffer) 483 { 484 struct dfs_filesystem *fs; 485 486 fs = dfs_filesystem_lookup(path); 487 if (fs != NULL) 488 { 489 if (fs->ops->statfs != NULL) 490 return fs->ops->statfs(fs, buffer); 491 } 492 493 return -1; 494 } 495 496 #ifdef RT_USING_DFS_MNTTABLE 497 int dfs_mount_table(void) 498 { 499 int index = 0; 500 501 while (1) 502 { 503 if (mount_table[index].path == NULL) break; 504 505 if (dfs_mount(mount_table[index].device_name, 506 mount_table[index].path, 507 mount_table[index].filesystemtype, 508 mount_table[index].rwflag, 509 mount_table[index].data) != 0) 510 { 511 rt_kprintf("mount fs[%s] on %s failed.\n", mount_table[index].filesystemtype, 512 mount_table[index].path); 513 return -RT_ERROR; 514 } 515 516 index ++; 517 } 518 return 0; 519 } 520 INIT_ENV_EXPORT(dfs_mount_table); 521 #endif 522 523 #ifdef RT_USING_FINSH 524 #include <finsh.h> 525 void mkfs(const char *fs_name, const char *device_name) 526 { 527 dfs_mkfs(fs_name, device_name); 528 } 529 FINSH_FUNCTION_EXPORT(mkfs, make a file system); 530 531 int df(const char *path) 532 { 533 int result; 534 int minor = 0; 535 long long cap; 536 struct statfs buffer; 537 538 int unit_index = 0; 539 char *unit_str[] = {"KB", "MB", "GB"}; 540 541 result = dfs_statfs(path ? path : NULL, &buffer); 542 if (result != 0) 543 { 544 rt_kprintf("dfs_statfs failed.\n"); 545 return -1; 546 } 547 548 cap = ((long long)buffer.f_bsize) * ((long long)buffer.f_bfree) / 1024LL; 549 for (unit_index = 0; unit_index < 2; unit_index ++) 550 { 551 if (cap < 1024) break; 552 553 minor = (cap % 1024) * 10 / 1024; /* only one decimal point */ 554 cap = cap / 1024; 555 } 556 557 rt_kprintf("disk free: %d.%d %s [ %d block, %d bytes per block ]\n", 558 (unsigned long)cap, minor, unit_str[unit_index], buffer.f_bfree, buffer.f_bsize); 559 return 0; 560 } 561 FINSH_FUNCTION_EXPORT(df, get disk free); 562 #endif 563 564 /* @} */ 565