dfs.c 12.3 KB
Newer Older
1
/*
2
 * Copyright (c) 2006-2021, RT-Thread Development Team
3
 *
4
 * SPDX-License-Identifier: Apache-2.0
5 6 7 8
 *
 * Change Logs:
 * Date           Author       Notes
 * 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 14 15
 */

#include <dfs.h>
#include <dfs_fs.h>
#include <dfs_file.h>
B
bernard 已提交
16
#include "dfs_private.h"
17 18 19
#ifdef RT_USING_LWP
#include <lwp.h>
#endif
20

21
#ifdef RT_USING_POSIX
22
#include <libc.h>
23
#endif /* RT_USING_POSIX */
24

25
/* Global variables */
B
bernard 已提交
26
const struct dfs_filesystem_ops *filesystem_operation_table[DFS_FILESYSTEM_TYPES_MAX];
27 28 29 30 31 32 33 34 35
struct dfs_filesystem filesystem_table[DFS_FILESYSTEMS_MAX];

/* device filesystem lock */
static struct rt_mutex fslock;

#ifdef DFS_USING_WORKDIR
char working_directory[DFS_PATH_MAX] = {"/"};
#endif

36 37
static struct dfs_fdtable _fdtab;
static int  fd_alloc(struct dfs_fdtable *fdt, int startfd);
38 39 40 41

/**
 * @addtogroup DFS
 */
42

43 44 45 46 47
/*@{*/

/**
 * this function will initialize device file system.
 */
B
Bernard Xiong 已提交
48
int dfs_init(void)
49
{
50 51 52
    static rt_bool_t init_ok = RT_FALSE;

    if (init_ok)
wuyangyong's avatar
wuyangyong 已提交
53 54
    {
        rt_kprintf("dfs already init.\n");
55
        return 0;
wuyangyong's avatar
wuyangyong 已提交
56 57
    }

58
    /* clear filesystem operations table */
B
bernard 已提交
59
    memset((void *)filesystem_operation_table, 0, sizeof(filesystem_operation_table));
60
    /* clear filesystem table */
B
bernard 已提交
61
    memset(filesystem_table, 0, sizeof(filesystem_table));
62
    /* clean fd table */
63
    memset(&_fdtab, 0, sizeof(_fdtab));
64

65 66
    /* create device filesystem lock */
    rt_mutex_init(&fslock, "fslock", RT_IPC_FLAG_FIFO);
67 68

#ifdef DFS_USING_WORKDIR
69
    /* set current working directory */
B
bernard 已提交
70
    memset(working_directory, 0, sizeof(working_directory));
71
    working_directory[0] = '/';
72
#endif
B
bernard 已提交
73 74 75 76 77 78 79 80 81 82 83 84

#ifdef RT_USING_DFS_DEVFS
    {
        extern int devfs_init(void);

        /* if enable devfs, initialize and mount it as soon as possible */
        devfs_init();

        dfs_mount(NULL, "/dev", "devfs", 0, 0);
    }
#endif

85 86
    init_ok = RT_TRUE;

B
bernard 已提交
87
    return 0;
88
}
B
bernard 已提交
89
INIT_PREV_EXPORT(dfs_init);
90 91 92 93 94 95

/**
 * this function will lock device file system.
 *
 * @note please don't invoke it on ISR.
 */
96
void dfs_lock(void)
97
{
98 99 100 101 102 103
    rt_err_t result = -RT_EBUSY;

    while (result == -RT_EBUSY)
    {
        result = rt_mutex_take(&fslock, RT_WAITING_FOREVER);
    }
104

105 106 107 108
    if (result != RT_EOK)
    {
        RT_ASSERT(0);
    }
109 110 111 112 113 114 115
}

/**
 * this function will lock device file system.
 *
 * @note please don't invoke it on ISR.
 */
116
void dfs_unlock(void)
117
{
118
    rt_mutex_release(&fslock);
119 120
}

121 122 123 124 125
static int fd_alloc(struct dfs_fdtable *fdt, int startfd)
{
    int idx;

    /* find an empty fd entry */
126
    for (idx = startfd; idx < (int)fdt->maxfd; idx++)
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
    {
        if (fdt->fds[idx] == RT_NULL)
            break;
        if (fdt->fds[idx]->ref_count == 0)
            break;
    }

    /* allocate a larger FD container */
    if (idx == fdt->maxfd && fdt->maxfd < DFS_FD_MAX)
    {
        int cnt, index;
        struct dfs_fd **fds;

        /* increase the number of FD with 4 step length */
        cnt = fdt->maxfd + 4;
142
        cnt = cnt > DFS_FD_MAX ? DFS_FD_MAX : cnt;
143

144
        fds = (struct dfs_fd **)rt_realloc(fdt->fds, cnt * sizeof(struct dfs_fd *));
Y
yangfasheng 已提交
145
        if (fds == NULL) goto __exit; /* return fdt->maxfd */
146 147 148 149 150 151 152 153 154 155 156 157

        /* clean the new allocated fds */
        for (index = fdt->maxfd; index < cnt; index ++)
        {
            fds[index] = NULL;
        }

        fdt->fds   = fds;
        fdt->maxfd = cnt;
    }

    /* allocate  'struct dfs_fd' */
158
    if (idx < (int)fdt->maxfd && fdt->fds[idx] == RT_NULL)
159
    {
160
        fdt->fds[idx] = (struct dfs_fd *)rt_calloc(1, sizeof(struct dfs_fd));
161 162 163 164
        if (fdt->fds[idx] == RT_NULL)
            idx = fdt->maxfd;
    }

Y
yangfasheng 已提交
165
__exit:
166 167 168
    return idx;
}

169 170 171 172 173 174 175 176
/**
 * @ingroup Fd
 * This function will allocate a file descriptor.
 *
 * @return -1 on failed or the allocated file descriptor.
 */
int fd_new(void)
{
177 178
    struct dfs_fd *d;
    int idx;
179
    struct dfs_fdtable *fdt;
180

181
    fdt = dfs_fdtable_get();
182 183
    /* lock filesystem */
    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 194
        goto __result;
    }
195

196
    d = fdt->fds[idx];
197
    d->ref_count = 1;
198
    d->magic = DFS_FD_MAGIC;
199 200

__result:
201
    dfs_unlock();
B
bernard 已提交
202
    return idx + DFS_FD_OFFSET;
203 204 205 206 207 208 209 210 211 212 213
}

/**
 * @ingroup Fd
 *
 * This function will return a file descriptor structure according to file
 * descriptor.
 *
 * @return NULL on on this file descriptor or the file descriptor structure
 * pointer.
 */
214
struct dfs_fd *fd_get(int fd)
215
{
216
    struct dfs_fd *d;
217
    struct dfs_fdtable *fdt;
218

219
#ifdef RT_USING_POSIX
220 221
    if ((0 <= fd) && (fd <= 2))
        fd = libc_stdio_get_console();
222
#endif /* RT_USING_POSIX */
223

224
    fdt = dfs_fdtable_get();
B
bernard 已提交
225
    fd = fd - DFS_FD_OFFSET;
226
    if (fd < 0 || fd >= (int)fdt->maxfd)
B
bernard 已提交
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();
B
bernard 已提交
236
        return NULL;
237 238
    }

239 240 241
    /* increase the reference count */
    d->ref_count ++;
    dfs_unlock();
242

243
    return d;
244 245 246 247 248 249 250
}

/**
 * @ingroup Fd
 *
 * This function will put the file descriptor.
 */
251
void fd_put(struct dfs_fd *fd)
252
{
B
bernard 已提交
253
    RT_ASSERT(fd != NULL);
254

255
    dfs_lock();
256

257
    fd->ref_count --;
258

259 260 261
    /* clear this fd entry */
    if (fd->ref_count == 0)
    {
262 263 264 265
        int index;
        struct dfs_fdtable *fdt;

        fdt = dfs_fdtable_get();
266
        for (index = 0; index < (int)fdt->maxfd; index ++)
267 268 269 270 271 272 273 274
        {
            if (fdt->fds[index] == fd)
            {
                rt_free(fd);
                fdt->fds[index] = 0;
                break;
            }
        }
275 276
    }
    dfs_unlock();
B
bernard 已提交
277
}
278

279
/**
280 281 282
 * @ingroup Fd
 *
 * This function will return whether this file has been opend.
283
 *
284 285 286 287
 * @param pathname the file path name.
 *
 * @return 0 on file has been open successfully, -1 on open failed.
 */
288
int fd_is_open(const char *pathname)
289
{
290 291 292 293
    char *fullpath;
    unsigned int index;
    struct dfs_filesystem *fs;
    struct dfs_fd *fd;
294
    struct dfs_fdtable *fdt;
295

296
    fdt = dfs_fdtable_get();
B
bernard 已提交
297 298
    fullpath = dfs_normalize_path(NULL, pathname);
    if (fullpath != NULL)
299 300 301
    {
        char *mountpath;
        fs = dfs_filesystem_lookup(fullpath);
B
bernard 已提交
302
        if (fs == NULL)
303 304
        {
            /* can't find mounted file system */
305
            rt_free(fullpath);
306 307 308 309 310 311 312

            return -1;
        }

        /* get file path name under mounted file system */
        if (fs->path[0] == '/' && fs->path[1] == '\0')
            mountpath = fullpath;
313
        else
314 315 316
            mountpath = fullpath + strlen(fs->path);

        dfs_lock();
B
bernard 已提交
317

318
        for (index = 0; index < fdt->maxfd; index++)
319
        {
320
            fd = fdt->fds[index];
Y
yangfasheng 已提交
321
            if (fd == NULL || fd->fops == NULL || fd->path == NULL) continue;
322

Y
yangfasheng 已提交
323
            if (fd->fs == fs && strcmp(fd->path, mountpath) == 0)
324 325 326 327 328 329 330 331 332 333 334 335 336 337
            {
                /* found file in file descriptor table */
                rt_free(fullpath);
                dfs_unlock();

                return 0;
            }
        }
        dfs_unlock();

        rt_free(fullpath);
    }

    return -1;
338 339 340 341 342 343 344 345 346 347
}

/**
 * this function will return a sub-path name under directory.
 *
 * @param directory the parent directory.
 * @param filename the filename.
 *
 * @return the subdir pointer in filename
 */
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 */
B
bernard 已提交
353
        return NULL;
354

355 356 357 358 359
    dir = filename + strlen(directory);
    if ((*dir != '/') && (dir != filename))
    {
        dir --;
    }
360

361
    return dir;
362
}
363
RTM_EXPORT(dfs_subdir);
364

365
/**
366 367
 * this function will normalize a path according to specified parent directory
 * and file name.
368 369 370 371
 *
 * @param directory the parent path
 * @param filename the file name
 *
372
 * @return the built full file path (absolute path)
373
 */
374
char *dfs_normalize_path(const char *directory, const char *filename)
375
{
376 377
    char *fullpath;
    char *dst0, *dst, *src;
378

379
    /* check parameters */
B
bernard 已提交
380
    RT_ASSERT(filename != NULL);
381 382

#ifdef DFS_USING_WORKDIR
B
bernard 已提交
383
    if (directory == NULL) /* shall use working directory */
384
        directory = &working_directory[0];
385
#else
B
bernard 已提交
386
    if ((directory == NULL) && (filename[0] != '/'))
387 388
    {
        rt_kprintf(NO_WORKING_DIR);
389

B
bernard 已提交
390
        return NULL;
391
    }
392 393
#endif

394 395
    if (filename[0] != '/') /* it's a absolute path, use it directly */
    {
396
        fullpath = (char *)rt_malloc(strlen(directory) + strlen(filename) + 2);
397

B
bernard 已提交
398 399
        if (fullpath == NULL)
            return NULL;
G
geniusgogo 已提交
400

401
        /* join path and file name */
402
        rt_snprintf(fullpath, strlen(directory) + strlen(filename) + 2,
403
                    "%s/%s", directory, filename);
404 405 406 407
    }
    else
    {
        fullpath = rt_strdup(filename); /* copy string */
408

B
bernard 已提交
409 410
        if (fullpath == NULL)
            return NULL;
411 412 413 414
    }

    src = fullpath;
    dst = fullpath;
415

416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
    dst0 = dst;
    while (1)
    {
        char c = *src;

        if (c == '.')
        {
            if (!src[1]) src ++; /* '.' and ends */
            else if (src[1] == '/')
            {
                /* './' case */
                src += 2;

                while ((*src == '/') && (*src != '\0'))
                    src ++;
                continue;
            }
            else if (src[1] == '.')
            {
                if (!src[2])
                {
                    /* '..' and ends case */
                    src += 2;
                    goto up_one;
                }
                else if (src[2] == '/')
                {
                    /* '../' case */
                    src += 3;

                    while ((*src == '/') && (*src != '\0'))
                        src ++;
                    goto up_one;
                }
            }
        }

        /* copy up the next '/' and erase all '/' */
        while ((c = *src++) != '\0' && c != '/')
            *dst ++ = c;

        if (c == '/')
        {
            *dst ++ = '/';
            while (c == '/')
                c = *src++;

            src --;
        }
        else if (!c)
            break;

        continue;
469 470

up_one:
471 472 473
        dst --;
        if (dst < dst0)
        {
474
            rt_free(fullpath);
B
bernard 已提交
475
            return NULL;
476 477 478 479 480 481 482 483 484 485 486 487
        }
        while (dst0 < dst && dst[-1] != '/')
            dst --;
    }

    *dst = '\0';

    /* remove '/' in the end of path if exist */
    dst --;
    if ((dst != fullpath) && (*dst == '/'))
        *dst = '\0';

488 489 490 491 492 493 494
    /* final check fullpath is not empty, for the special path of lwext "/.." */
    if ('\0' == fullpath[0])
    {
        fullpath[0] = '/';
        fullpath[1] = '\0';
    }

495
    return fullpath;
496
}
497
RTM_EXPORT(dfs_normalize_path);
498 499 500 501

/**
 * This function will get the file descriptor table of current process.
 */
502
struct dfs_fdtable *dfs_fdtable_get(void)
503 504 505
{
    struct dfs_fdtable *fdt;
#ifdef RT_USING_LWP
506
    struct rt_lwp *lwp;
507

H
heyuanjie 已提交
508
    lwp = (struct rt_lwp *)rt_thread_self()->lwp;
509
    if (lwp)
510 511 512
        fdt = &lwp->fdt;
    else
        fdt = &_fdtab;
513 514 515 516 517 518 519
#else
    fdt = &_fdtab;
#endif

    return fdt;
}

520
#ifdef RT_USING_FINSH
B
bernard 已提交
521 522 523 524
#include <finsh.h>
int list_fd(void)
{
    int index;
525
    struct dfs_fdtable *fd_table;
526

527 528
    fd_table = dfs_fdtable_get();
    if (!fd_table) return -1;
B
bernard 已提交
529 530

    rt_enter_critical();
531

532 533
    rt_kprintf("fd type    ref magic  path\n");
    rt_kprintf("-- ------  --- ----- ------\n");
534
    for (index = 0; index < (int)fd_table->maxfd; index ++)
B
bernard 已提交
535
    {
536
        struct dfs_fd *fd = fd_table->fds[index];
B
bernard 已提交
537

538
        if (fd && fd->fops)
B
bernard 已提交
539
        {
540
            rt_kprintf("%2d ", index + DFS_FD_OFFSET);
541 542 543 544
            if (fd->type == FT_DIRECTORY)    rt_kprintf("%-7.7s ", "dir");
            else if (fd->type == FT_REGULAR) rt_kprintf("%-7.7s ", "file");
            else if (fd->type == FT_SOCKET)  rt_kprintf("%-7.7s ", "socket");
            else if (fd->type == FT_USER)    rt_kprintf("%-7.7s ", "user");
545
            else if (fd->type == FT_DEVICE)   rt_kprintf("%-7.7s ", "device");
546 547 548
            else rt_kprintf("%-8.8s ", "unknown");
            rt_kprintf("%3d ", fd->ref_count);
            rt_kprintf("%04x  ", fd->magic);
B
bernard 已提交
549 550
            if (fd->path)
            {
551 552 553 554 555
                rt_kprintf("%s\n", fd->path);
            }
            else
            {
                rt_kprintf("\n");
B
bernard 已提交
556 557 558 559 560 561 562 563
            }
        }
    }
    rt_exit_critical();

    return 0;
}
MSH_CMD_EXPORT(list_fd, list file descriptor);
564
#endif
565 566
/*@}*/