dfs.c 10.0 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 25 26 27 28
 */

#include <dfs.h>
#include <dfs_fs.h>
#include <dfs_file.h>
B
bernard 已提交
29
#include "dfs_private.h"
30 31

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

struct dfs_fd fd_table[DFS_FD_MAX];

/**
 * @addtogroup DFS
 */
47

48 49 50 51 52
/*@{*/

/**
 * this function will initialize device file system.
 */
B
Bernard Xiong 已提交
53
int dfs_init(void)
54
{
55
    /* clear filesystem operations table */
B
bernard 已提交
56
    memset((void *)filesystem_operation_table, 0, sizeof(filesystem_operation_table));
57
    /* clear filesystem table */
B
bernard 已提交
58
    memset(filesystem_table, 0, sizeof(filesystem_table));
59
    /* clean fd table */
B
bernard 已提交
60
    memset(fd_table, 0, sizeof(fd_table));
61

62 63
    /* create device filesystem lock */
    rt_mutex_init(&fslock, "fslock", RT_IPC_FLAG_FIFO);
64 65

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

#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;
83
}
B
bernard 已提交
84
INIT_PREV_EXPORT(dfs_init);
85 86 87 88 89 90

/**
 * this function will lock device file system.
 *
 * @note please don't invoke it on ISR.
 */
91
void dfs_lock(void)
92
{
93
    rt_err_t result;
94

95 96 97 98 99
    result = rt_mutex_take(&fslock, RT_WAITING_FOREVER);
    if (result != RT_EOK)
    {
        RT_ASSERT(0);
    }
100 101 102 103 104 105 106
}

/**
 * this function will lock device file system.
 *
 * @note please don't invoke it on ISR.
 */
107
void dfs_unlock(void)
108
{
109
    rt_mutex_release(&fslock);
110 111 112 113 114 115 116 117 118 119
}

/**
 * @ingroup Fd
 * This function will allocate a file descriptor.
 *
 * @return -1 on failed or the allocated file descriptor.
 */
int fd_new(void)
{
120 121
    struct dfs_fd *d;
    int idx;
122

123 124
    /* lock filesystem */
    dfs_lock();
125

126 127
    /* find an empty fd entry */
    for (idx = 0; idx < DFS_FD_MAX && fd_table[idx].ref_count > 0; idx++);
128

129 130 131
    /* can't find an empty fd entry */
    if (idx == DFS_FD_MAX)
    {
132
        idx = -(1 + DFS_FD_OFFSET);
133 134
        goto __result;
    }
135

136 137
    d = &(fd_table[idx]);
    d->ref_count = 1;
138
    d->magic = DFS_FD_MAGIC;
139 140

__result:
141
    dfs_unlock();
B
bernard 已提交
142
    return idx + DFS_FD_OFFSET;
143 144 145 146 147 148 149 150 151 152 153
}

/**
 * @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.
 */
154
struct dfs_fd *fd_get(int fd)
155
{
156
    struct dfs_fd *d;
157

B
bernard 已提交
158
    fd = fd - DFS_FD_OFFSET;
159
    if (fd < 0 || fd >= DFS_FD_MAX)
B
bernard 已提交
160
        return NULL;
161

162 163
    dfs_lock();
    d = &fd_table[fd];
164

165 166 167 168
    /* check dfs_fd valid or not */
    if (d->magic != DFS_FD_MAGIC)
    {
        dfs_unlock();
B
bernard 已提交
169
        return NULL;
170 171
    }

172 173 174
    /* increase the reference count */
    d->ref_count ++;
    dfs_unlock();
175

176
    return d;
177 178 179 180 181 182 183
}

/**
 * @ingroup Fd
 *
 * This function will put the file descriptor.
 */
184
void fd_put(struct dfs_fd *fd)
185
{
B
bernard 已提交
186
    RT_ASSERT(fd != NULL);
187

188 189
    dfs_lock();
    fd->ref_count --;
190

191 192 193
    /* clear this fd entry */
    if (fd->ref_count == 0)
    {
B
bernard 已提交
194
        memset(fd, 0, sizeof(struct dfs_fd));
195 196
    }
    dfs_unlock();
B
bernard 已提交
197
}
198

199
/**
200 201 202
 * @ingroup Fd
 *
 * This function will return whether this file has been opend.
203
 *
204 205 206 207
 * @param pathname the file path name.
 *
 * @return 0 on file has been open successfully, -1 on open failed.
 */
208
int fd_is_open(const char *pathname)
209
{
210 211 212 213 214
    char *fullpath;
    unsigned int index;
    struct dfs_filesystem *fs;
    struct dfs_fd *fd;

B
bernard 已提交
215 216
    fullpath = dfs_normalize_path(NULL, pathname);
    if (fullpath != NULL)
217 218 219
    {
        char *mountpath;
        fs = dfs_filesystem_lookup(fullpath);
B
bernard 已提交
220
        if (fs == NULL)
221 222
        {
            /* can't find mounted file system */
223
            rt_free(fullpath);
224 225 226 227 228 229 230

            return -1;
        }

        /* get file path name under mounted file system */
        if (fs->path[0] == '/' && fs->path[1] == '\0')
            mountpath = fullpath;
231
        else
232 233 234
            mountpath = fullpath + strlen(fs->path);

        dfs_lock();
B
bernard 已提交
235

236 237 238
        for (index = 0; index < DFS_FD_MAX; index++)
        {
            fd = &(fd_table[index]);
B
bernard 已提交
239
            if (fd->fops == NULL)
240 241
                continue;

B
bernard 已提交
242
            if (fd->fops == fs->ops->fops && strcmp(fd->path, mountpath) == 0)
243 244 245 246 247 248 249 250 251 252 253 254 255 256
            {
                /* found file in file descriptor table */
                rt_free(fullpath);
                dfs_unlock();

                return 0;
            }
        }
        dfs_unlock();

        rt_free(fullpath);
    }

    return -1;
257 258 259 260 261 262 263 264 265 266
}

/**
 * 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
 */
267
const char *dfs_subdir(const char *directory, const char *filename)
268
{
269
    const char *dir;
270

271
    if (strlen(directory) == strlen(filename)) /* it's a same path */
B
bernard 已提交
272
        return NULL;
273

274 275 276 277 278
    dir = filename + strlen(directory);
    if ((*dir != '/') && (dir != filename))
    {
        dir --;
    }
279

280
    return dir;
281
}
282
RTM_EXPORT(dfs_subdir);
283

284
/**
285 286
 * this function will normalize a path according to specified parent directory
 * and file name.
287 288 289 290
 *
 * @param directory the parent path
 * @param filename the file name
 *
291
 * @return the built full file path (absolute path)
292
 */
293
char *dfs_normalize_path(const char *directory, const char *filename)
294
{
295 296
    char *fullpath;
    char *dst0, *dst, *src;
297

298
    /* check parameters */
B
bernard 已提交
299
    RT_ASSERT(filename != NULL);
300 301

#ifdef DFS_USING_WORKDIR
B
bernard 已提交
302
    if (directory == NULL) /* shall use working directory */
303
        directory = &working_directory[0];
304
#else
B
bernard 已提交
305
    if ((directory == NULL) && (filename[0] != '/'))
306 307
    {
        rt_kprintf(NO_WORKING_DIR);
308

B
bernard 已提交
309
        return NULL;
310
    }
311 312
#endif

313 314 315 316
    if (filename[0] != '/') /* it's a absolute path, use it directly */
    {
        fullpath = rt_malloc(strlen(directory) + strlen(filename) + 2);

B
bernard 已提交
317 318
        if (fullpath == NULL)
            return NULL;
G
geniusgogo 已提交
319

320
        /* join path and file name */
321
        rt_snprintf(fullpath, strlen(directory) + strlen(filename) + 2,
322 323 324 325 326
            "%s/%s", directory, filename);
    }
    else
    {
        fullpath = rt_strdup(filename); /* copy string */
327

B
bernard 已提交
328 329
        if (fullpath == NULL)
            return NULL;
330 331 332 333
    }

    src = fullpath;
    dst = fullpath;
334

335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387
    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;
388 389

up_one:
390 391 392
        dst --;
        if (dst < dst0)
        {
393
            rt_free(fullpath);
B
bernard 已提交
394
            return NULL;
395 396 397 398 399 400 401 402 403 404 405 406
        }
        while (dst0 < dst && dst[-1] != '/')
            dst --;
    }

    *dst = '\0';

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

407 408 409 410 411 412 413
    /* final check fullpath is not empty, for the special path of lwext "/.." */
    if ('\0' == fullpath[0])
    {
        fullpath[0] = '/';
        fullpath[1] = '\0';
    }

414
    return fullpath;
415
}
416
RTM_EXPORT(dfs_normalize_path);
417
#ifdef RT_USING_FINSH
B
bernard 已提交
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
#include <finsh.h>
int list_fd(void)
{
    int index;

    rt_enter_critical();

    for (index = 0; index < DFS_FD_MAX; index ++)
    {
        struct dfs_fd *fd = &(fd_table[index]);

        if (fd->fops)
        {
            rt_kprintf("--fd: %d--", index);
            if (fd->type == FT_DIRECTORY) rt_kprintf("[dir]\n");
            if (fd->type == FT_REGULAR)   rt_kprintf("[file]\n");
            if (fd->type == FT_SOCKET)    rt_kprintf("[socket]\n");
            if (fd->type == FT_USER)      rt_kprintf("[user]\n");
            rt_kprintf("refcount=%d\n", fd->ref_count);
            rt_kprintf("magic=0x%04x\n", fd->magic);
            if (fd->path)
            {
                rt_kprintf("path: %s\n", fd->path);
            }
        }
    }
    rt_exit_critical();

    return 0;
}
MSH_CMD_EXPORT(list_fd, list file descriptor);
449
#endif
450 451
/*@}*/