dfs.c 9.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 23 24 25 26 27
 *
 * Change Logs:
 * Date           Author       Notes
 * 2005-02-22     Bernard      The first version.
 */

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

/* Global variables */
B
bernard 已提交
31
const struct dfs_filesystem_ops *filesystem_operation_table[DFS_FILESYSTEM_TYPES_MAX];
32 33 34 35 36 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

struct dfs_fd fd_table[DFS_FD_MAX];

/**
 * @addtogroup DFS
 */
46

47 48 49 50 51
/*@{*/

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            return -1;
        }

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

        dfs_lock();
B
bernard 已提交
234

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

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

                return 0;
            }
        }
        dfs_unlock();

        rt_free(fullpath);
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

    src = fullpath;
    dst = fullpath;
333

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
    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;
387 388

up_one:
389 390 391
        dst --;
        if (dst < dst0)
        {
392
            rt_free(fullpath);
B
bernard 已提交
393
            return NULL;
394 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';

    return fullpath;
407
}
408
RTM_EXPORT(dfs_normalize_path);
409
#ifdef RT_USING_FINSH
B
bernard 已提交
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
#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);
441
#endif
442 443
/*@}*/