dfs.c 12.5 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.
 */
wuyangyong's avatar
wuyangyong 已提交
58
static volatile uint8_t init_ok = 0;
B
Bernard Xiong 已提交
59
int dfs_init(void)
60
{
wuyangyong's avatar
wuyangyong 已提交
61 62 63 64 65 66 67
    if(init_ok)
    {
        rt_kprintf("dfs already init.\n");
        return 0;        
    }
    init_ok = 1;	

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 95

#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;
96
}
B
bernard 已提交
97
INIT_PREV_EXPORT(dfs_init);
98 99 100 101 102 103

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

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

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

/**
 * this function will lock device file system.
 *
 * @note please don't invoke it on ISR.
 */
124
void dfs_unlock(void)
125
{
126
    rt_mutex_release(&fslock);
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 169 170 171 172 173 174 175 176
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;
}

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

189
    fdt = dfs_fdtable_get();
190 191
    /* lock filesystem */
    dfs_lock();
192

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

196
    /* can't find an empty fd entry */
197
    if (idx == fdt->maxfd)
198
    {
199
        idx = -(1 + DFS_FD_OFFSET);
200 201
        goto __result;
    }
202

203
    d = fdt->fds[idx];
204
    d->ref_count = 1;
205
    d->magic = DFS_FD_MAGIC;
206 207

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

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

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

231
    dfs_lock();
232
    d = fdt->fds[fd];
233

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

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

245
    return d;
246 247 248 249 250 251 252
}

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

257
    dfs_lock();
258

259
    fd->ref_count --;
260

261 262 263
    /* clear this fd entry */
    if (fd->ref_count == 0)
    {
264 265 266 267 268 269 270 271 272 273 274 275 276
        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;
            }
        }
277 278
    }
    dfs_unlock();
B
bernard 已提交
279
}
280

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

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

            return -1;
        }

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

        dfs_lock();
B
bernard 已提交
319

320
        for (index = 0; index < fdt->maxfd; index++)
321
        {
322 323
            fd = fdt->fds[index];
            if (fd == NULL) continue;
324

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

                return 0;
            }
        }
        dfs_unlock();

        rt_free(fullpath);
    }

    return -1;
340 341 342 343 344 345 346 347 348 349
}

/**
 * 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
 */
350
const char *dfs_subdir(const char *directory, const char *filename)
351
{
352
    const char *dir;
353

354
    if (strlen(directory) == strlen(filename)) /* it's a same path */
B
bernard 已提交
355
        return NULL;
356

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

363
    return dir;
364
}
365
RTM_EXPORT(dfs_subdir);
366

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

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

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

B
bernard 已提交
392
        return NULL;
393
    }
394 395
#endif

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

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

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

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

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

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

    *dst = '\0';

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

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

497
    return fullpath;
498
}
499
RTM_EXPORT(dfs_normalize_path);
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521

/**
 * 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;
}

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

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

B
Bluebear233 已提交
540
        if (fd != RT_NULL)
B
bernard 已提交
541
        {
542 543 544 545 546 547 548 549
            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 已提交
550 551
            if (fd->path)
            {
552 553 554 555 556
                rt_kprintf("%s\n", fd->path);
            }
            else
            {
                rt_kprintf("\n");
B
bernard 已提交
557 558 559 560 561 562 563 564
            }
        }
    }
    rt_exit_critical();

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