dfs.c 12.7 KB
Newer Older
1 2 3
/*
 * File      : dfs.c
 * This file is part of Device File System in RT-Thread RTOS
4
 * COPYRIGHT (C) 2004-2012, RT-Thread Development Team
5
 *
Y
yiyue.fang 已提交
6 7 8 9 10 11 12 13 14 15 16 17 18
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 20 21 22
 *
 * Change Logs:
 * Date           Author       Notes
 * 2005-02-22     Bernard      The first version.
23
 * 2017-12-11     Bernard      Use rt_free to instead of free in fd_is_open().
24
 * 2018-03-20     Heyuanjie    dynamic allocation FD
25 26 27 28 29
 */

#include <dfs.h>
#include <dfs_fs.h>
#include <dfs_file.h>
B
bernard 已提交
30
#include "dfs_private.h"
31 32 33
#ifdef RT_USING_LWP
#include <lwp.h>
#endif
34 35

/* Global variables */
B
bernard 已提交
36
const struct dfs_filesystem_ops *filesystem_operation_table[DFS_FILESYSTEM_TYPES_MAX];
37 38 39 40 41 42 43 44 45
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

46 47
static struct dfs_fdtable _fdtab;
static int  fd_alloc(struct dfs_fdtable *fdt, int startfd);
48 49 50 51

/**
 * @addtogroup DFS
 */
52

53 54 55 56 57
/*@{*/

/**
 * this function will initialize device file system.
 */
B
Bernard Xiong 已提交
58
int dfs_init(void)
59
{
60 61 62
    static rt_bool_t init_ok = RT_FALSE;

    if (init_ok)
wuyangyong's avatar
wuyangyong 已提交
63 64
    {
        rt_kprintf("dfs already init.\n");
65
        return 0;
wuyangyong's avatar
wuyangyong 已提交
66 67
    }

68
    /* clear filesystem operations table */
B
bernard 已提交
69
    memset((void *)filesystem_operation_table, 0, sizeof(filesystem_operation_table));
70
    /* clear filesystem table */
B
bernard 已提交
71
    memset(filesystem_table, 0, sizeof(filesystem_table));
72
    /* clean fd table */
73
    memset(&_fdtab, 0, sizeof(_fdtab));
74

75 76
    /* create device filesystem lock */
    rt_mutex_init(&fslock, "fslock", RT_IPC_FLAG_FIFO);
77 78

#ifdef DFS_USING_WORKDIR
79
    /* set current working directory */
B
bernard 已提交
80
    memset(working_directory, 0, sizeof(working_directory));
81
    working_directory[0] = '/';
82
#endif
B
bernard 已提交
83 84 85 86 87 88 89 90 91 92 93 94

#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

95 96
    init_ok = RT_TRUE;

B
bernard 已提交
97
    return 0;
98
}
B
bernard 已提交
99
INIT_PREV_EXPORT(dfs_init);
100 101 102 103 104 105

/**
 * this function will lock device file system.
 *
 * @note please don't invoke it on ISR.
 */
106
void dfs_lock(void)
107
{
108 109 110 111 112 113
    rt_err_t result = -RT_EBUSY;

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

115 116 117 118
    if (result != RT_EOK)
    {
        RT_ASSERT(0);
    }
119 120 121 122 123 124 125
}

/**
 * this function will lock device file system.
 *
 * @note please don't invoke it on ISR.
 */
126
void dfs_unlock(void)
127
{
128
    rt_mutex_release(&fslock);
129 130
}

131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
static int fd_alloc(struct dfs_fdtable *fdt, int startfd)
{
    int idx;

    /* find an empty fd entry */
    for (idx = startfd; idx < fdt->maxfd; idx++)
    {
        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;
        cnt = cnt > DFS_FD_MAX? DFS_FD_MAX : cnt;

        fds = rt_realloc(fdt->fds, cnt * sizeof(struct dfs_fd *));
        if (fds == NULL) goto __out; /* return fdt->maxfd */

        /* 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' */
    if (idx < fdt->maxfd &&fdt->fds[idx] == RT_NULL)
    {
        fdt->fds[idx] = rt_malloc(sizeof(struct dfs_fd));
        if (fdt->fds[idx] == RT_NULL)
            idx = fdt->maxfd;
    }

__out:
    return idx;
}

179 180 181 182 183 184 185 186
/**
 * @ingroup Fd
 * This function will allocate a file descriptor.
 *
 * @return -1 on failed or the allocated file descriptor.
 */
int fd_new(void)
{
187 188
    struct dfs_fd *d;
    int idx;
189
    struct dfs_fdtable *fdt;
190

191
    fdt = dfs_fdtable_get();
192 193
    /* lock filesystem */
    dfs_lock();
194

195
    /* find an empty fd entry */
196
    idx = fd_alloc(fdt, 0);
197

198
    /* can't find an empty fd entry */
199
    if (idx == fdt->maxfd)
200
    {
201
        idx = -(1 + DFS_FD_OFFSET);
armink_ztl's avatar
armink_ztl 已提交
202
        dbg_log(DBG_ERROR, "DFS fd new is failed! Could not found an empty fd entry.");
203 204
        goto __result;
    }
205

206
    d = fdt->fds[idx];
207
    d->ref_count = 1;
208
    d->magic = DFS_FD_MAGIC;
209 210

__result:
211
    dfs_unlock();
B
bernard 已提交
212
    return idx + DFS_FD_OFFSET;
213 214 215 216 217 218 219 220 221 222 223
}

/**
 * @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.
 */
224
struct dfs_fd *fd_get(int fd)
225
{
226
    struct dfs_fd *d;
227
    struct dfs_fdtable *fdt;
228

229
    fdt = dfs_fdtable_get();
B
bernard 已提交
230
    fd = fd - DFS_FD_OFFSET;
231
    if (fd < 0 || fd >= fdt->maxfd)
B
bernard 已提交
232
        return NULL;
233

234
    dfs_lock();
235
    d = fdt->fds[fd];
236

237 238 239 240
    /* check dfs_fd valid or not */
    if (d->magic != DFS_FD_MAGIC)
    {
        dfs_unlock();
B
bernard 已提交
241
        return NULL;
242 243
    }

244 245 246
    /* increase the reference count */
    d->ref_count ++;
    dfs_unlock();
247

248
    return d;
249 250 251 252 253 254 255
}

/**
 * @ingroup Fd
 *
 * This function will put the file descriptor.
 */
256
void fd_put(struct dfs_fd *fd)
257
{
B
bernard 已提交
258
    RT_ASSERT(fd != NULL);
259

260
    dfs_lock();
261

262
    fd->ref_count --;
263

264 265 266
    /* clear this fd entry */
    if (fd->ref_count == 0)
    {
267 268 269 270 271 272 273 274 275 276 277 278 279
        int index;
        struct dfs_fdtable *fdt;

        fdt = dfs_fdtable_get();
        for (index = 0; index < fdt->maxfd; index ++)
        {
            if (fdt->fds[index] == fd)
            {
                rt_free(fd);
                fdt->fds[index] = 0;
                break;
            }
        }
280 281
    }
    dfs_unlock();
B
bernard 已提交
282
}
283

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

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

            return -1;
        }

        /* get file path name under mounted file system */
        if (fs->path[0] == '/' && fs->path[1] == '\0')
            mountpath = fullpath;
318
        else
319 320 321
            mountpath = fullpath + strlen(fs->path);

        dfs_lock();
B
bernard 已提交
322

323
        for (index = 0; index < fdt->maxfd; index++)
324
        {
325 326
            fd = fdt->fds[index];
            if (fd == NULL) continue;
327

B
bernard 已提交
328
            if (fd->fops == fs->ops->fops && strcmp(fd->path, mountpath) == 0)
329 330 331 332 333 334 335 336 337 338 339 340 341 342
            {
                /* found file in file descriptor table */
                rt_free(fullpath);
                dfs_unlock();

                return 0;
            }
        }
        dfs_unlock();

        rt_free(fullpath);
    }

    return -1;
343 344 345 346 347 348 349 350 351 352
}

/**
 * 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
 */
353
const char *dfs_subdir(const char *directory, const char *filename)
354
{
355
    const char *dir;
356

357
    if (strlen(directory) == strlen(filename)) /* it's a same path */
B
bernard 已提交
358
        return NULL;
359

360 361 362 363 364
    dir = filename + strlen(directory);
    if ((*dir != '/') && (dir != filename))
    {
        dir --;
    }
365

366
    return dir;
367
}
368
RTM_EXPORT(dfs_subdir);
369

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

384
    /* check parameters */
B
bernard 已提交
385
    RT_ASSERT(filename != NULL);
386 387

#ifdef DFS_USING_WORKDIR
B
bernard 已提交
388
    if (directory == NULL) /* shall use working directory */
389
        directory = &working_directory[0];
390
#else
B
bernard 已提交
391
    if ((directory == NULL) && (filename[0] != '/'))
392 393
    {
        rt_kprintf(NO_WORKING_DIR);
394

B
bernard 已提交
395
        return NULL;
396
    }
397 398
#endif

399 400 401 402
    if (filename[0] != '/') /* it's a absolute path, use it directly */
    {
        fullpath = rt_malloc(strlen(directory) + strlen(filename) + 2);

B
bernard 已提交
403 404
        if (fullpath == NULL)
            return NULL;
G
geniusgogo 已提交
405

406
        /* join path and file name */
407
        rt_snprintf(fullpath, strlen(directory) + strlen(filename) + 2,
408 409 410 411 412
            "%s/%s", directory, filename);
    }
    else
    {
        fullpath = rt_strdup(filename); /* copy string */
413

B
bernard 已提交
414 415
        if (fullpath == NULL)
            return NULL;
416 417 418 419
    }

    src = fullpath;
    dst = fullpath;
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 469 470 471 472 473
    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;
474 475

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

    *dst = '\0';

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

493 494 495 496 497 498 499
    /* final check fullpath is not empty, for the special path of lwext "/.." */
    if ('\0' == fullpath[0])
    {
        fullpath[0] = '/';
        fullpath[1] = '\0';
    }

500
    return fullpath;
501
}
502
RTM_EXPORT(dfs_normalize_path);
503 504 505 506 507 508 509 510

/**
 * This function will get the file descriptor table of current process.
 */
struct dfs_fdtable* dfs_fdtable_get(void)
{
    struct dfs_fdtable *fdt;
#ifdef RT_USING_LWP
511
    struct rt_lwp *lwp;
512

513
    lwp = (struct rt_lwp *)rt_thread_self()->user_data;
514
    if (lwp)
515 516 517
        fdt = &lwp->fdt;
    else
        fdt = &_fdtab;
518 519 520 521 522 523 524
#else
    fdt = &_fdtab;
#endif

    return fdt;
}

525
#ifdef RT_USING_FINSH
B
bernard 已提交
526 527 528 529
#include <finsh.h>
int list_fd(void)
{
    int index;
530 531 532 533
    struct dfs_fdtable *fd_table;
    
    fd_table = dfs_fdtable_get();
    if (!fd_table) return -1;
B
bernard 已提交
534 535

    rt_enter_critical();
536 537 538
    
    rt_kprintf("fd type    ref magic  path\n");
    rt_kprintf("-- ------  --- ----- ------\n");
539
    for (index = 0; index < fd_table->maxfd; index ++)
B
bernard 已提交
540
    {
541
        struct dfs_fd *fd = fd_table->fds[index];
B
bernard 已提交
542

B
Bluebear233 已提交
543
        if (fd != RT_NULL)
B
bernard 已提交
544
        {
545 546 547 548 549 550 551 552
            rt_kprintf("%2d ", index);
            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");
            else rt_kprintf("%-8.8s ", "unknown");
            rt_kprintf("%3d ", fd->ref_count);
            rt_kprintf("%04x  ", fd->magic);
B
bernard 已提交
553 554
            if (fd->path)
            {
555 556 557 558 559
                rt_kprintf("%s\n", fd->path);
            }
            else
            {
                rt_kprintf("\n");
B
bernard 已提交
560 561 562 563 564 565 566 567
            }
        }
    }
    rt_exit_critical();

    return 0;
}
MSH_CMD_EXPORT(list_fd, list file descriptor);
568
#endif
569 570
/*@}*/