dfs.c 12.4 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
    /* clear filesystem operations table */
B
bernard 已提交
61
    memset((void *)filesystem_operation_table, 0, sizeof(filesystem_operation_table));
62
    /* clear filesystem table */
B
bernard 已提交
63
    memset(filesystem_table, 0, sizeof(filesystem_table));
64
    /* clean fd table */
65
    memset(&_fdtab, 0, sizeof(_fdtab));
66

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

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

#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

    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 126 127 128 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
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;
}

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 193
        goto __result;
    }
194

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

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

/**
 * @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.
 */
213
struct dfs_fd *fd_get(int fd)
214
{
215
    struct dfs_fd *d;
216
    struct dfs_fdtable *fdt;
217

218
    fdt = dfs_fdtable_get();
B
bernard 已提交
219
    fd = fd - DFS_FD_OFFSET;
220
    if (fd < 0 || fd >= fdt->maxfd)
B
bernard 已提交
221
        return NULL;
222

223
    dfs_lock();
224
    d = fdt->fds[fd];
225

226 227 228 229
    /* check dfs_fd valid or not */
    if (d->magic != DFS_FD_MAGIC)
    {
        dfs_unlock();
B
bernard 已提交
230
        return NULL;
231 232
    }

233 234 235
    /* increase the reference count */
    d->ref_count ++;
    dfs_unlock();
236

237
    return d;
238 239 240 241 242 243 244
}

/**
 * @ingroup Fd
 *
 * This function will put the file descriptor.
 */
245
void fd_put(struct dfs_fd *fd)
246
{
B
bernard 已提交
247
    RT_ASSERT(fd != NULL);
248

249
    dfs_lock();
250

251
    fd->ref_count --;
252

253 254 255
    /* clear this fd entry */
    if (fd->ref_count == 0)
    {
256 257 258 259 260 261 262 263 264 265 266 267 268
        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;
            }
        }
269 270
    }
    dfs_unlock();
B
bernard 已提交
271
}
272

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

290
    fdt = dfs_fdtable_get();
B
bernard 已提交
291 292
    fullpath = dfs_normalize_path(NULL, pathname);
    if (fullpath != NULL)
293 294 295
    {
        char *mountpath;
        fs = dfs_filesystem_lookup(fullpath);
B
bernard 已提交
296
        if (fs == NULL)
297 298
        {
            /* can't find mounted file system */
299
            rt_free(fullpath);
300 301 302 303 304 305 306

            return -1;
        }

        /* get file path name under mounted file system */
        if (fs->path[0] == '/' && fs->path[1] == '\0')
            mountpath = fullpath;
307
        else
308 309 310
            mountpath = fullpath + strlen(fs->path);

        dfs_lock();
B
bernard 已提交
311

312
        for (index = 0; index < fdt->maxfd; index++)
313
        {
314 315
            fd = fdt->fds[index];
            if (fd == NULL) continue;
316

B
bernard 已提交
317
            if (fd->fops == fs->ops->fops && strcmp(fd->path, mountpath) == 0)
318 319 320 321 322 323 324 325 326 327 328 329 330 331
            {
                /* found file in file descriptor table */
                rt_free(fullpath);
                dfs_unlock();

                return 0;
            }
        }
        dfs_unlock();

        rt_free(fullpath);
    }

    return -1;
332 333 334 335 336 337 338 339 340 341
}

/**
 * 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
 */
342
const char *dfs_subdir(const char *directory, const char *filename)
343
{
344
    const char *dir;
345

346
    if (strlen(directory) == strlen(filename)) /* it's a same path */
B
bernard 已提交
347
        return NULL;
348

349 350 351 352 353
    dir = filename + strlen(directory);
    if ((*dir != '/') && (dir != filename))
    {
        dir --;
    }
354

355
    return dir;
356
}
357
RTM_EXPORT(dfs_subdir);
358

359
/**
360 361
 * this function will normalize a path according to specified parent directory
 * and file name.
362 363 364 365
 *
 * @param directory the parent path
 * @param filename the file name
 *
366
 * @return the built full file path (absolute path)
367
 */
368
char *dfs_normalize_path(const char *directory, const char *filename)
369
{
370 371
    char *fullpath;
    char *dst0, *dst, *src;
372

373
    /* check parameters */
B
bernard 已提交
374
    RT_ASSERT(filename != NULL);
375 376

#ifdef DFS_USING_WORKDIR
B
bernard 已提交
377
    if (directory == NULL) /* shall use working directory */
378
        directory = &working_directory[0];
379
#else
B
bernard 已提交
380
    if ((directory == NULL) && (filename[0] != '/'))
381 382
    {
        rt_kprintf(NO_WORKING_DIR);
383

B
bernard 已提交
384
        return NULL;
385
    }
386 387
#endif

388 389 390 391
    if (filename[0] != '/') /* it's a absolute path, use it directly */
    {
        fullpath = rt_malloc(strlen(directory) + strlen(filename) + 2);

B
bernard 已提交
392 393
        if (fullpath == NULL)
            return NULL;
G
geniusgogo 已提交
394

395
        /* join path and file name */
396
        rt_snprintf(fullpath, strlen(directory) + strlen(filename) + 2,
397 398 399 400 401
            "%s/%s", directory, filename);
    }
    else
    {
        fullpath = rt_strdup(filename); /* copy string */
402

B
bernard 已提交
403 404
        if (fullpath == NULL)
            return NULL;
405 406 407 408
    }

    src = fullpath;
    dst = fullpath;
409

410 411 412 413 414 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
    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;
463 464

up_one:
465 466 467
        dst --;
        if (dst < dst0)
        {
468
            rt_free(fullpath);
B
bernard 已提交
469
            return NULL;
470 471 472 473 474 475 476 477 478 479 480 481
        }
        while (dst0 < dst && dst[-1] != '/')
            dst --;
    }

    *dst = '\0';

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

482 483 484 485 486 487 488
    /* final check fullpath is not empty, for the special path of lwext "/.." */
    if ('\0' == fullpath[0])
    {
        fullpath[0] = '/';
        fullpath[1] = '\0';
    }

489
    return fullpath;
490
}
491
RTM_EXPORT(dfs_normalize_path);
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513

/**
 * 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
	struct rt_lwp *lwp;

	lwp = (struct rt_lwp *)rt_thread_self()->user_data;
    if (lwp)
		fdt = &lwp->fdt;
	else
		fdt = &_fdtab;
#else
    fdt = &_fdtab;
#endif

    return fdt;
}

514
#ifdef RT_USING_FINSH
B
bernard 已提交
515 516 517 518
#include <finsh.h>
int list_fd(void)
{
    int index;
519 520 521 522
    struct dfs_fdtable *fd_table;
    
    fd_table = dfs_fdtable_get();
    if (!fd_table) return -1;
B
bernard 已提交
523 524

    rt_enter_critical();
525 526 527
    
    rt_kprintf("fd type    ref magic  path\n");
    rt_kprintf("-- ------  --- ----- ------\n");
528
    for (index = 0; index < fd_table->maxfd; index ++)
B
bernard 已提交
529
    {
530
        struct dfs_fd *fd = fd_table->fds[index];
B
bernard 已提交
531 532 533

        if (fd->fops)
        {
534 535 536 537 538 539 540 541
            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 已提交
542 543
            if (fd->path)
            {
544 545 546 547 548
                rt_kprintf("%s\n", fd->path);
            }
            else
            {
                rt_kprintf("\n");
B
bernard 已提交
549 550 551 552 553 554 555 556
            }
        }
    }
    rt_exit_critical();

    return 0;
}
MSH_CMD_EXPORT(list_fd, list file descriptor);
557
#endif
558 559
/*@}*/