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 * 2017-12-11 Bernard Use rt_free to instead of free in fd_is_open(). 10 * 2018-03-20 Heyuanjie dynamic allocation FD 11 */ 12 13 #include <dfs.h> 14 #include <dfs_fs.h> 15 #include <dfs_file.h> 16 #include "dfs_private.h" 17 #ifdef RT_USING_LWP 18 #include <lwp.h> 19 #endif 20 21 #if defined(RT_USING_DFS_DEVFS) && defined(RT_USING_POSIX) 22 #include <libc.h> 23 #endif 24 25 /* Global variables */ 26 const struct dfs_filesystem_ops *filesystem_operation_table[DFS_FILESYSTEM_TYPES_MAX]; 27 struct dfs_filesystem filesystem_table[DFS_FILESYSTEMS_MAX]; 28 29 /* device filesystem lock */ 30 static struct rt_mutex fslock; 31 32 #ifdef DFS_USING_WORKDIR 33 char working_directory[DFS_PATH_MAX] = {"/"}; 34 #endif 35 36 static struct dfs_fdtable _fdtab; 37 static int fd_alloc(struct dfs_fdtable *fdt, int startfd); 38 39 /** 40 * @addtogroup DFS 41 */ 42 43 /*@{*/ 44 45 /** 46 * this function will initialize device file system. 47 */ 48 int dfs_init(void) 49 { 50 static rt_bool_t init_ok = RT_FALSE; 51 52 if (init_ok) 53 { 54 rt_kprintf("dfs already init.\n"); 55 return 0; 56 } 57 58 /* clear filesystem operations table */ 59 memset((void *)filesystem_operation_table, 0, sizeof(filesystem_operation_table)); 60 /* clear filesystem table */ 61 memset(filesystem_table, 0, sizeof(filesystem_table)); 62 /* clean fd table */ 63 memset(&_fdtab, 0, sizeof(_fdtab)); 64 65 /* create device filesystem lock */ 66 rt_mutex_init(&fslock, "fslock", RT_IPC_FLAG_FIFO); 67 68 #ifdef DFS_USING_WORKDIR 69 /* set current working directory */ 70 memset(working_directory, 0, sizeof(working_directory)); 71 working_directory[0] = '/'; 72 #endif 73 74 #ifdef RT_USING_DFS_DEVFS 75 { 76 extern int devfs_init(void); 77 78 /* if enable devfs, initialize and mount it as soon as possible */ 79 devfs_init(); 80 81 dfs_mount(NULL, "/dev", "devfs", 0, 0); 82 } 83 #endif 84 85 init_ok = RT_TRUE; 86 87 return 0; 88 } 89 INIT_PREV_EXPORT(dfs_init); 90 91 /** 92 * this function will lock device file system. 93 * 94 * @note please don't invoke it on ISR. 95 */ 96 void dfs_lock(void) 97 { 98 rt_err_t result = -RT_EBUSY; 99 100 while (result == -RT_EBUSY) 101 { 102 result = rt_mutex_take(&fslock, RT_WAITING_FOREVER); 103 } 104 105 if (result != RT_EOK) 106 { 107 RT_ASSERT(0); 108 } 109 } 110 111 /** 112 * this function will lock device file system. 113 * 114 * @note please don't invoke it on ISR. 115 */ 116 void dfs_unlock(void) 117 { 118 rt_mutex_release(&fslock); 119 } 120 121 static int fd_alloc(struct dfs_fdtable *fdt, int startfd) 122 { 123 int idx; 124 125 /* find an empty fd entry */ 126 for (idx = startfd; idx < fdt->maxfd; idx++) 127 { 128 if (fdt->fds[idx] == RT_NULL) 129 break; 130 if (fdt->fds[idx]->ref_count == 0) 131 break; 132 } 133 134 /* allocate a larger FD container */ 135 if (idx == fdt->maxfd && fdt->maxfd < DFS_FD_MAX) 136 { 137 int cnt, index; 138 struct dfs_fd **fds; 139 140 /* increase the number of FD with 4 step length */ 141 cnt = fdt->maxfd + 4; 142 cnt = cnt > DFS_FD_MAX? DFS_FD_MAX : cnt; 143 144 fds = rt_realloc(fdt->fds, cnt * sizeof(struct dfs_fd *)); 145 if (fds == NULL) goto __exit; /* return fdt->maxfd */ 146 147 /* clean the new allocated fds */ 148 for (index = fdt->maxfd; index < cnt; index ++) 149 { 150 fds[index] = NULL; 151 } 152 153 fdt->fds = fds; 154 fdt->maxfd = cnt; 155 } 156 157 /* allocate 'struct dfs_fd' */ 158 if (idx < fdt->maxfd &&fdt->fds[idx] == RT_NULL) 159 { 160 fdt->fds[idx] = rt_calloc(1, sizeof(struct dfs_fd)); 161 if (fdt->fds[idx] == RT_NULL) 162 idx = fdt->maxfd; 163 } 164 165 __exit: 166 return idx; 167 } 168 169 /** 170 * @ingroup Fd 171 * This function will allocate a file descriptor. 172 * 173 * @return -1 on failed or the allocated file descriptor. 174 */ 175 int fd_new(void) 176 { 177 struct dfs_fd *d; 178 int idx; 179 struct dfs_fdtable *fdt; 180 181 fdt = dfs_fdtable_get(); 182 /* lock filesystem */ 183 dfs_lock(); 184 185 /* find an empty fd entry */ 186 idx = fd_alloc(fdt, 0); 187 188 /* can't find an empty fd entry */ 189 if (idx == fdt->maxfd) 190 { 191 idx = -(1 + DFS_FD_OFFSET); 192 LOG_E( "DFS fd new is failed! Could not found an empty fd entry."); 193 goto __result; 194 } 195 196 d = fdt->fds[idx]; 197 d->ref_count = 1; 198 d->magic = DFS_FD_MAGIC; 199 200 __result: 201 dfs_unlock(); 202 return idx + DFS_FD_OFFSET; 203 } 204 205 /** 206 * @ingroup Fd 207 * 208 * This function will return a file descriptor structure according to file 209 * descriptor. 210 * 211 * @return NULL on on this file descriptor or the file descriptor structure 212 * pointer. 213 */ 214 struct dfs_fd *fd_get(int fd) 215 { 216 struct dfs_fd *d; 217 struct dfs_fdtable *fdt; 218 219 #if defined(RT_USING_DFS_DEVFS) && defined(RT_USING_POSIX) 220 if ((0 <= fd) && (fd <= 2)) 221 fd = libc_stdio_get_console(); 222 #endif 223 224 fdt = dfs_fdtable_get(); 225 fd = fd - DFS_FD_OFFSET; 226 if (fd < 0 || fd >= fdt->maxfd) 227 return NULL; 228 229 dfs_lock(); 230 d = fdt->fds[fd]; 231 232 /* check dfs_fd valid or not */ 233 if ((d == NULL) || (d->magic != DFS_FD_MAGIC)) 234 { 235 dfs_unlock(); 236 return NULL; 237 } 238 239 /* increase the reference count */ 240 d->ref_count ++; 241 dfs_unlock(); 242 243 return d; 244 } 245 246 /** 247 * @ingroup Fd 248 * 249 * This function will put the file descriptor. 250 */ 251 void fd_put(struct dfs_fd *fd) 252 { 253 RT_ASSERT(fd != NULL); 254 255 dfs_lock(); 256 257 fd->ref_count --; 258 259 /* clear this fd entry */ 260 if (fd->ref_count == 0) 261 { 262 int index; 263 struct dfs_fdtable *fdt; 264 265 fdt = dfs_fdtable_get(); 266 for (index = 0; index < fdt->maxfd; index ++) 267 { 268 if (fdt->fds[index] == fd) 269 { 270 rt_free(fd); 271 fdt->fds[index] = 0; 272 break; 273 } 274 } 275 } 276 dfs_unlock(); 277 } 278 279 /** 280 * @ingroup Fd 281 * 282 * This function will return whether this file has been opend. 283 * 284 * @param pathname the file path name. 285 * 286 * @return 0 on file has been open successfully, -1 on open failed. 287 */ 288 int fd_is_open(const char *pathname) 289 { 290 char *fullpath; 291 unsigned int index; 292 struct dfs_filesystem *fs; 293 struct dfs_fd *fd; 294 struct dfs_fdtable *fdt; 295 296 fdt = dfs_fdtable_get(); 297 fullpath = dfs_normalize_path(NULL, pathname); 298 if (fullpath != NULL) 299 { 300 char *mountpath; 301 fs = dfs_filesystem_lookup(fullpath); 302 if (fs == NULL) 303 { 304 /* can't find mounted file system */ 305 rt_free(fullpath); 306 307 return -1; 308 } 309 310 /* get file path name under mounted file system */ 311 if (fs->path[0] == '/' && fs->path[1] == '\0') 312 mountpath = fullpath; 313 else 314 mountpath = fullpath + strlen(fs->path); 315 316 dfs_lock(); 317 318 for (index = 0; index < fdt->maxfd; index++) 319 { 320 fd = fdt->fds[index]; 321 if (fd == NULL || fd->fops == NULL || fd->path == NULL) continue; 322 323 if (fd->fs == fs && strcmp(fd->path, mountpath) == 0) 324 { 325 /* found file in file descriptor table */ 326 rt_free(fullpath); 327 dfs_unlock(); 328 329 return 0; 330 } 331 } 332 dfs_unlock(); 333 334 rt_free(fullpath); 335 } 336 337 return -1; 338 } 339 340 /** 341 * this function will return a sub-path name under directory. 342 * 343 * @param directory the parent directory. 344 * @param filename the filename. 345 * 346 * @return the subdir pointer in filename 347 */ 348 const char *dfs_subdir(const char *directory, const char *filename) 349 { 350 const char *dir; 351 352 if (strlen(directory) == strlen(filename)) /* it's a same path */ 353 return NULL; 354 355 dir = filename + strlen(directory); 356 if ((*dir != '/') && (dir != filename)) 357 { 358 dir --; 359 } 360 361 return dir; 362 } 363 RTM_EXPORT(dfs_subdir); 364 365 /** 366 * this function will normalize a path according to specified parent directory 367 * and file name. 368 * 369 * @param directory the parent path 370 * @param filename the file name 371 * 372 * @return the built full file path (absolute path) 373 */ 374 char *dfs_normalize_path(const char *directory, const char *filename) 375 { 376 char *fullpath; 377 char *dst0, *dst, *src; 378 379 /* check parameters */ 380 RT_ASSERT(filename != NULL); 381 382 #ifdef DFS_USING_WORKDIR 383 if (directory == NULL) /* shall use working directory */ 384 directory = &working_directory[0]; 385 #else 386 if ((directory == NULL) && (filename[0] != '/')) 387 { 388 rt_kprintf(NO_WORKING_DIR); 389 390 return NULL; 391 } 392 #endif 393 394 if (filename[0] != '/') /* it's a absolute path, use it directly */ 395 { 396 fullpath = rt_malloc(strlen(directory) + strlen(filename) + 2); 397 398 if (fullpath == NULL) 399 return NULL; 400 401 /* join path and file name */ 402 rt_snprintf(fullpath, strlen(directory) + strlen(filename) + 2, 403 "%s/%s", directory, filename); 404 } 405 else 406 { 407 fullpath = rt_strdup(filename); /* copy string */ 408 409 if (fullpath == NULL) 410 return NULL; 411 } 412 413 src = fullpath; 414 dst = fullpath; 415 416 dst0 = dst; 417 while (1) 418 { 419 char c = *src; 420 421 if (c == '.') 422 { 423 if (!src[1]) src ++; /* '.' and ends */ 424 else if (src[1] == '/') 425 { 426 /* './' case */ 427 src += 2; 428 429 while ((*src == '/') && (*src != '\0')) 430 src ++; 431 continue; 432 } 433 else if (src[1] == '.') 434 { 435 if (!src[2]) 436 { 437 /* '..' and ends case */ 438 src += 2; 439 goto up_one; 440 } 441 else if (src[2] == '/') 442 { 443 /* '../' case */ 444 src += 3; 445 446 while ((*src == '/') && (*src != '\0')) 447 src ++; 448 goto up_one; 449 } 450 } 451 } 452 453 /* copy up the next '/' and erase all '/' */ 454 while ((c = *src++) != '\0' && c != '/') 455 *dst ++ = c; 456 457 if (c == '/') 458 { 459 *dst ++ = '/'; 460 while (c == '/') 461 c = *src++; 462 463 src --; 464 } 465 else if (!c) 466 break; 467 468 continue; 469 470 up_one: 471 dst --; 472 if (dst < dst0) 473 { 474 rt_free(fullpath); 475 return NULL; 476 } 477 while (dst0 < dst && dst[-1] != '/') 478 dst --; 479 } 480 481 *dst = '\0'; 482 483 /* remove '/' in the end of path if exist */ 484 dst --; 485 if ((dst != fullpath) && (*dst == '/')) 486 *dst = '\0'; 487 488 /* final check fullpath is not empty, for the special path of lwext "/.." */ 489 if ('\0' == fullpath[0]) 490 { 491 fullpath[0] = '/'; 492 fullpath[1] = '\0'; 493 } 494 495 return fullpath; 496 } 497 RTM_EXPORT(dfs_normalize_path); 498 499 /** 500 * This function will get the file descriptor table of current process. 501 */ 502 struct dfs_fdtable* dfs_fdtable_get(void) 503 { 504 struct dfs_fdtable *fdt; 505 #ifdef RT_USING_LWP 506 struct rt_lwp *lwp; 507 508 lwp = (struct rt_lwp *)rt_thread_self()->lwp; 509 if (lwp) 510 fdt = &lwp->fdt; 511 else 512 fdt = &_fdtab; 513 #else 514 fdt = &_fdtab; 515 #endif 516 517 return fdt; 518 } 519 520 #ifdef RT_USING_FINSH 521 #include <finsh.h> 522 int list_fd(void) 523 { 524 int index; 525 struct dfs_fdtable *fd_table; 526 527 fd_table = dfs_fdtable_get(); 528 if (!fd_table) return -1; 529 530 rt_enter_critical(); 531 532 rt_kprintf("fd type ref magic path\n"); 533 rt_kprintf("-- ------ --- ----- ------\n"); 534 for (index = 0; index < fd_table->maxfd; index ++) 535 { 536 struct dfs_fd *fd = fd_table->fds[index]; 537 538 if (fd && fd->fops) 539 { 540 rt_kprintf("%2d ", index); 541 if (fd->type == FT_DIRECTORY) rt_kprintf("%-7.7s ", "dir"); 542 else if (fd->type == FT_REGULAR) rt_kprintf("%-7.7s ", "file"); 543 else if (fd->type == FT_SOCKET) rt_kprintf("%-7.7s ", "socket"); 544 else if (fd->type == FT_USER) rt_kprintf("%-7.7s ", "user"); 545 else rt_kprintf("%-8.8s ", "unknown"); 546 rt_kprintf("%3d ", fd->ref_count); 547 rt_kprintf("%04x ", fd->magic); 548 if (fd->path) 549 { 550 rt_kprintf("%s\n", fd->path); 551 } 552 else 553 { 554 rt_kprintf("\n"); 555 } 556 } 557 } 558 rt_exit_critical(); 559 560 return 0; 561 } 562 MSH_CMD_EXPORT(list_fd, list file descriptor); 563 #endif 564 /*@}*/ 565 566