全部文档
OneOS简介 硬件支持 编译构造工具 API参考文档 高级语言 用户编程手册 应用笔记 FAQ

文件系统


简介

文件系统是一套实现了数据的存储、分级组织、访问和获取等操作的抽象数据类型,是一种用于向用户提供底层数据访问的机制。文件系统存储的基本单位是文件,即数据是按照一个个文件的方式进行组织。当文件比较多时,将导致文件繁多,不易分类、重名的问题。而文件夹作为一个容纳多个文件的容器而存在。

在本操作系统中,为了能够支持多种文件系统,实现了虚拟文件系统。对上而言,可以提供统一的POSIX接口,方便用户使用。对下而言,可以注册不同的具体文件系统类型。整个文件系统的层次架构如下:

POSIX接口层

POSIX表示可移植操作系统接口(Portable OperaTIng System Interface of UNIX,缩写为 POSIX ),POSIX标准定义了操作系统应该为应用程序提供的接口标准,是IEEE为要在各种UNIX操作系统上运行的软件而定义的一系列API标准的总称,其正式称呼为IEEE 1003。POSIX标准意在期望获得源代码级别的软件可移植性。换句话说,为一个POSIX兼容的操作系统编写的程序,应该可以在任何其它的POSIX操作系统(即使是来自另一个厂商)上编译执行。

本操作系统支持POSIX标准接口,因此可以很方便的将 Linux/Unix 的程序移植到本操作系统上。

虚拟文件系统

本操作系统支持多种文件系统,如FATFS、CUTEFS、NFS、YAFFS2等。不同的文件系统有各自不同的管理文件的方式,对于用户而言,最好不用去关心每种文件系统的文件存取方式。因此本操作系统在POSIX接口层和具体文件系统之间引入了一个抽象层,这个抽象层称之为虚拟文件系统(Virtual File System)。

为了能够支持多种实际文件系统,VFS定义了各种文件系统都支持的基本的、概念上的接口和数据结构,VFS不用关心具体文件系统的实现细节。

具体文件系统

本操作系统目前支持以下文件系统,方便用户根据实际情况选用:

FATFS:本组件支持的FAT文件系统是移植的一个兼容微软FAT格式的通用FAT文件系统模块(presented by ChaN),非常适合小型嵌入式设备开发。
CUTEFS:一种小巧的文件系统,支持各种文件及目录操作,目前需要配合RAMDISK使用。
DEVFS:设备文件系统,可以将系统中的设备在/dev文件夹下虚拟成文件。
NFS:网络文件系统,本系统移植了一个网络文件系统客户端,可以通过操作本地文件系统一样的方式去访问服务器端的文件。
JFFS2:JFFS(Journalling Flash FileSystem)文件系统最早是由瑞典Axis Communications AB在嵌入式系统上开发的Flash文件系统,而JFFS2是RedHat公司在JFFS的基础上开发的文件系统。这是一种日志型文件系统,主要用于Nor Flash,支持数据压缩功能,支持磨损均衡可延长FLASH使用寿命,支持垃圾回收机制。
YAFFS2:YAFFS(Yet Another Flash File System)文件系统是一种针对Nand Flash特性而设计的嵌入式文件系统。目前有两个版本,Yaffs1支持page大小512byte的Nand Flash,Yaffs2支持更大page(2kbyte)的Nand Flash。这也是一种日志型文件系统,主要用于Nand Flash,支持磨损均衡,支持垃圾回收。

设备抽象层

设备抽象层将物理设备,如SD Card、Flash等抽象成文件系统能够访问的设备,例如FAT文件系统要求存储设备是块设备类型。不同的文件系统一般是独立于存储设备驱动而实现的,因此需要把底层存储设备驱动接口与具体文件系统对接起来,文件系统才能正常使用。


初始化和挂载文件系统

文件系统要能够正常使用,一般需要先经过以下几个步骤:

  • 初始化VFS组件

  • 注册具体的文件系统

  • 在存储器上创建块设备

  • 在块设备上格式化文件系统

  • 挂载设备到某个路径

  • 文件系统不再使用时,可以卸载

初始化VFS组件

VFS组件的的初始化是由函数vfs_init()完成。vfs_init() 函数会初始化 VFS 所需的相关资源,创建一些关键的数据结构(包括文件系统操作表,文件系统表,文件描述符表等)。有了这些数据结构,VFS便能在系统中记录和访问特定的文件系统,并获得对文件的操作方法。本操作系统中有自动初始化功能,该函数将会被自动调用。

注册文件系统

用户根据自己的需要选择具体的文件系统并注册到VFS中,通过函数vfs_register()实现,它会将具体文件系统的操作接口与VFS中的文件系统操作表关联起来。本操作系统中在配置时如果打开了某种文件系统,那么在初始化阶段将会调用该函数注册该文件系统。

将存储设备注册为块设备

存储设备初始化后,需要将该存储设备注册为块设备,通过函数os_device_register()实现,它会将设备的操作接口与设备名关联起来。以后需要设备时,可以使用os_device_find()通过设备名找到实际设备,进而可以获取到实际设备的操作接口。

格式化文件系统

不同具体文件系统的组织方式是不一样的,所以在使用具体文件系统之前,需要将块设备按照某种文件系统进行格式化,通过函数vfs_mkfs()实现。它会在设备的某个区域上,写入一些符合具体文件系统格式的数据,表明该文件系统的一些特征。后续在访问该设备时,就能识别到该设备上的数据是否是按照该文件系统组织的,才能根据该文件系统的操作接口去访问文件。

挂载文件系统

我们访问文件时是根据路径来访问的,所以在使用文件系统前,还需要将存储文件的设备与一个路径关联起来,这就是文件系统的挂载,通过函数vfs_mount()实现。之后就可以通过这个路径来访问存储设备。

卸载文件系统

当某个文件系统不再需要使用时,可以通过函数vfs_unmount()卸载掉。


文件系统操作接口

接口 说明
vfs_register 注册某种类型的文件系统到VFS中
vfs_mkfs 将设备格式化成某种文件系统
vfs_mount 将设备挂载到某个路径上
vfs_unmount 卸载某个路径上的文件系统

vfs_register

该函数用于注册某种类型的文件系统到VFS中,函数原型如下:

int vfs_register(const struct vfs_filesystem_ops *ops);
参数 说明
ops VFS操作结构体,该结构体中包含了实际文件系统对应的操作接口,如mount/unmount/open/close/read/write等。每个具体的文件系统不一定支持所有接口。
返回 说明
0 注册成功
-1 注册失败

vfs_mkfs

该函数用于将设备格式化成某种文件系统,函数原型如下:

int vfs_mkfs(const char *fs_name, const char *device_name);
参数 说明
fs_name 文件系统类型名字,例如:对于FATFS名字为fat,对于JFFS2文件系统名字为jffs2,对于YAFFS文件系统名字为yaffs;CUTEFS、DEVFS、NFS不需要格式化,不支持此操作
device_name 设备名
返回 说明
0 格式化成功
其它值 格式化失败

vfs_mount

该函数用于将设备挂载到某个路径上,函数原型如下:

int vfs_mount(const char   *device_name,
              const char   *path,
              const char   *filesystemtype,
              unsigned long rwflag,
              const void   *data);
参数 说明
device_name 设备名
path 挂载路径
filesystemtype 文件系统类型
rwflag 读写设备的标记,暂未使用该参数
data 私有数据
返回 说明
0 挂载成功
其它值 挂载失败

vfs_unmount

该函数用于将某个路径上的文件系统卸载掉,函数原型如下:

int vfs_unmount(const char *path);
参数 说明
path 文件系统的路径(与挂载时的路径相同)
返回 说明
0 卸载成功
其它值 卸载失败

POSIX文件操作接口

接口 说明
open 打开指定文件,并返回文件描述符
close 关闭文件
read 从文件中读取指定长度的数据
write 写指定长度的数据到文件
lseek 设置读写位置
rename 重命名文件
unlink 移除文件
stat 根据文件路径获取文件信息
fstat 根据文件描述符获取文件信息
fsync 同步缓存数据到存储设备
fcntl 对文件描述符的控制操作
ioctl 对(设备)文件的控制操作
access 查看用户访问权限

open

该函数用于打开指定文件,并返回文件描述符,函数原型如下:

int open(const char *file, int flags, ...);
参数 说明
file 文件名
flags 打开文件的方式
... 可变参数
返回 说明
非负值 文件描述符
-1 打开失败,errno可通过os_get_errno()查看

close

该函数用于关闭指定文件,函数原型如下:

int close(int fd);
参数 说明
fd 文件描述符
返回 说明
0 关闭成功
-1 关闭失败,errno可通过os_get_errno()查看

read

该函数用于从文件中读取指定长度的数据,函数原型如下:

int read(int fd, void *buf, size_t len);
参数 说明
fd 文件描述符
buf 保存读取到数据的缓存区
len 期望读取数据的长度
返回 说明
正数 实际读取到数据的长度
0 已到达文件尾部
-1 失败,errno可通过os_get_errno()查看

write

该函数用于将将指定长度的数据写入文件,函数原型如下:

int write(int fd, const void *buf, size_t len);
参数 说明
fd 文件描述符
buf 待写入数据的地址
len 写入数据的长度
返回 说明
非负数 实际写入数据的长度
-1 写入失败,errno可通过os_get_errno()查看

lseek

该函数用于修改文件的读写位置,函数原型如下:

off_t lseek(int fd, off_t offset, int whence);
参数 说明
fd 文件描述符
offset 位置偏移量
whence 偏移的位置,包括SEEK_SET、SEEK_CUR、SEEK_END
返回 说明
非负数 当前读写位置距离文件头部的字节数
-1 失败,errno可通过os_get_errno()查看

rename

该函数用于重命名文件,按照如下语义实现:a)将一个文件重命名为一个目录,或将一个目录重命名为一个文件将会失败;b)将旧文件重命名为一个已经存在的新文件将会成功,原本存在的新文件会先被删除;c)将旧目录重命名为一个已经存在的、且非空的新目录会失败;d)将旧目录重命名为一个已经存在的、空的新目录会成功,原本存在的新目录会先被删除;e)新路径以旧路径作为前缀会失败;f)新路径包含不存在的父目录会失败。注意:在某些文件系统上,该操作无法保证原子性。函数原型如下:

int rename(const char *old, const char *new);
参数 说明
old 旧文件名
new 新文件名
返回 说明
0 成功
-1 失败,errno可通过os_get_errno()查看

该函数用于移除文件,函数原型如下:

int unlink(const char *pathname);
参数 说明
pathname 文件名
返回 说明
0 成功
-1 失败,errno可通过os_get_errno()查看

stat

该函数用于根据文件路径获取文件状态信息,函数原型如下:

int stat(const char *path, struct stat *buf);
参数 说明
path 文件名(文件路径)
buf 用于保存获取到的文件信息的缓存区地址
返回 说明
0 成功
-1 失败,errno可通过os_get_errno()查看

fstat

该函数用于根据文件描述符获取文件状态信息,函数原型如下:

int fstat(int fd, struct stat *buf);
参数 说明
fd 文件描述符
buf 用于保存获取到的文件信息的缓存区地址
返回 说明
0 成功
-1 失败,errno可通过os_get_errno()查看

fsync

该函数用于同步缓存数据到存储设备,函数原型如下:

int fsync(int fd);
参数 说明
fd 文件描述符
返回 说明
0 成功
-1 失败,errno可通过os_get_errno()查看

fcntl

该函数用于设置或者获取文件描述符的特殊属性,函数原型如下:

int fcntl(int fd, int cmd, ...);
参数 说明
fd 文件描述符
cmd 控制命令,目前支持F_GETFL、F_SETFL
... 可变参数
返回 说明
非负数 若是设置属性,正确时返回0;若是读取属性,正确时返回该属性值
-1 失败,errno可通过os_get_errno()查看

ioctl

该函数用于对(设备)文件的控制操作,函数原型如下:

int ioctl(int fd, int cmd, ...);
参数 说明
fd 文件描述符
cmd 控制命令,依赖于具体的(设备)文件的支持
... 可变参数
返回 说明
非负数 取决于具体的命令
-1 失败,errno可通过os_get_errno()查看

access

该函数用于查看是否有权限访问指定文件,函数原型如下:

int access(const char *path, int amode);
参数 说明
path 文件名(文件路径)
amode 检查模式,该参数暂未使用
返回 说明
0 成功,目前仅支持查看文件是否存在,忽略读、写、可执行权限
-1 失败,errno可通过os_get_errno()查看

POSIX目录操作接口

接口 说明
mkdir 创建目录
rmdir 删除目录
opendir 打开目录
closedir 关闭目录
readdir 读取目录内容
telldir 获取与目录流相关联的当前位置
seekdir 设置下一个读取目录(readdir)操作的位置
rewinddir 重置目录流的读取位置到目录开头
getcwd 获取当前工作路径
chdir 修改当前工作路径

mkdir

该函数用于创建目录,函数原型如下:

int mkdir(const char *path, mode_t mode);
参数 说明
path 路径
mode 权限,该参数暂未使用
返回 说明
0 成功
-1 失败,errno可通过os_get_errno()查看

rmdir

该函数用于删除目录,函数原型如下:

int rmdir(const char *path);
参数 说明
path 路径
返回 说明
0 成功
-1 失败,errno可通过os_get_errno()查看

opendir

该函数用于打开目录,函数原型如下:

DIR *opendir(const char *name)
参数 说明
name 路径
返回 说明
非NULL值 打开成功,返回目录流指针
NULL 打开失败,errno可通过os_get_errno()查看

closedir

该函数用于关闭目录,函数原型如下:

int closedir(DIR *d);
参数 说明
d 目录流指针
返回 说明
0 关闭成功
-1 关闭失败,errno可通过os_get_errno()查看

readdir

该函数用于读取目录内容,且每调用一次该函数,则目录条目的指针就会移向下一个,函数原型如下:

struct dirent *readdir(DIR *d);
参数 说明
d 目录流指针
返回 说明
非NULL值 目录条目的指针
NULL 错误或者已经到达目录结尾,errno可通过os_get_errno()查看

telldir

该函数用于获取与目录流相关联的当前位置,函数原型如下:

long telldir(DIR *d);
参数 说明
d 目录流指针
返回 说明
其它值 该返回值记录着一个目录流的当前位置,代表距离目录文件开头的偏移量。此偏移量可以用于seekdir()函数
-1 错误,errno可通过os_get_errno()查看

seekdir

该函数用于设置下一个读取目录(readdir)操作的位置,函数原型如下:

void seekdir(DIR *d, off_t offset);
参数 说明
d 目录流指针
offset 偏移量

rewinddir

该函数用于重置目录流的读取位置到目录开头,函数原型如下:

void rewinddir(DIR *d);
参数 说明
d 目录流指针

getcwd

该函数用于获取当前工作路径,函数原型如下:

char *getcwd(char *buf, size_t size);
参数 说明
buf 保存路径的缓存区
size 缓存区的大小
返回 说明
char * 缓存区的指针

chdir

该函数用于修改当前工作路径,函数原型如下:

int chdir(const char *path);
参数 说明
path 新路径
返回 说明
0 成功
-1 失败,errno可通过os_get_errno()查看

文件操作shell命令

支持常用的文件操作命令,具体如下:

命令 用法 说明
ls ls [DIRNAME] 列出DIRNAME目录下的信息;若无参数DIRNAME,则列出当前目录下信息
cp cp <SOURCE> <DEST> 把SOURCE文件或文件夹复制到DEST
mv mv <SOURCE> <DEST> 把SOURCE文件或文件夹重命名成DEST
echo echo <string> [FILE] 把字符串sting写入到FILE中;若无参数FILE,则回显字符string串
cat cat <FILE> ... 查看文件FILE的内容,可同时查看多个文件内容
rm rm <FILE> ... 删除文件或文件夹的内容,可同时删除多个;删除文件夹前,需确认该文件夹为空
cd cd [PATH] 切换到PATH所在路径;若无参数PATH,则仍为当前路径
pwd pwd 显示当前工作路径
mkdir mkdir <DIRNAME> 创建目录DIRNAME

另外还支持几个跟文件系统相关的命令,具体如下:

命令 用法 说明
mkfs mkfs [-t TYPE] <DEVICENAME> 在设备DEVICENAME上创建TYPE类型的文件系统;当前TYPE支持fat、jffs2、yaffs;若无-t TYPE参数时,相当于mkfs -t fat <DEVICENAME>
df df [PATH] 查看磁盘剩余空间

使用示例

文件操作接口使用示例

本例演示了文件操作常用接口的使用方法

#include <oneos_config.h>
#include <os_dbg.h>
#include <shell.h>
#include <string.h>
#include <vfs_posix.h>

#define TEST_TAG  "TEST"
#define FILE_NAME "test.txt"
#define FILE_NAME_NEW "test_new.txt"

void file_operation_sample(void)
{
    int fd, size, str_len;
    char *str[3] = {"1234567890", "ABCDEFGHIJ", "abcdefghij"};
    char bufread[35];
    off_t offset = 0;
    char* str_ptr;    
    struct stat filestat;

    fd = open(FILE_NAME, O_RDWR | O_CREAT);
    if (fd < 0)
    {        
        LOG_E(TEST_TAG, "open file err");
        return;
    }
    else
    {        
        LOG_W(TEST_TAG, "open(create) file:%s", FILE_NAME);
        str_ptr = str[0];
        str_len = strlen(str_ptr);
        size = write(fd, str_ptr, str_len);
        LOG_W(TEST_TAG, "write %d byte from offset %d :%s", size, offset, str_ptr);
    }

    offset = 20;
    if (lseek(fd, offset, SEEK_SET) != -1)
    {
        str_ptr = str[1];
        str_len = strlen(str_ptr);
        size = write(fd, str_ptr, str_len);
        LOG_W(TEST_TAG, "write %d byte from offset %d :%s", size, offset, str_ptr);
    }

    offset = 10;
    if (lseek(fd, offset, SEEK_SET) != -1)
    {
        str_ptr = str[2];
        str_len = strlen(str_ptr);
        size = write(fd, str_ptr, str_len);
        LOG_W(TEST_TAG, "write %d byte from offset %d :%s", size, offset, str_ptr);
    }

    offset = 0;
    if (lseek(fd, offset, SEEK_SET) != -1)
    {
        memset(bufread, 0, sizeof(bufread));
        size = read(fd, bufread, sizeof(bufread));        
        LOG_W(TEST_TAG, "read %d byte from offset %d :%s", size, offset, bufread);
    }

    if(fstat(fd, &filestat) != -1)
    {
        LOG_W(TEST_TAG, "the filesize of %s is :%d", FILE_NAME, filestat.st_size);
    }

    if(close(fd) != -1)
    {
        LOG_W(TEST_TAG, "close file:%s", FILE_NAME);
    }

    if(rename(FILE_NAME, FILE_NAME_NEW) != -1)
    {
        LOG_W(TEST_TAG, "rename %s to %s", FILE_NAME, FILE_NAME_NEW);
    }

    if(stat(FILE_NAME_NEW, &filestat) != -1)
    {
        LOG_W(TEST_TAG, "the filesize of %s is :%d", FILE_NAME_NEW, filestat.st_size);
    }

    if (unlink(FILE_NAME_NEW) != -1)
    {
        LOG_W(TEST_TAG, "rm file:%s", FILE_NAME_NEW);
    }
}

SH_CMD_EXPORT(test_file, file_operation_sample, "test for file operation");

运行结果如下:

sh />test_file
W/TEST: open(create) file:test.txt
W/TEST: write 10 byte from offset 0 :1234567890
W/TEST: write 10 byte from offset 20 :ABCDEFGHIJ
W/TEST: write 10 byte from offset 10 :abcdefghij
W/TEST: read 30 byte from offset 0 :1234567890abcdefghijABCDEFGHIJ
W/TEST: the filesize of test.txt is :30
W/TEST: close file:test.txt
W/TEST: rename test.txt to test_new.txt
W/TEST: the filesize of test_new.txt is :30
W/TEST: rm file:test_new.txt

目录操作接口使用示例

本例演示了常用目录操作接口的使用方法

#include <oneos_config.h>
#include <os_dbg.h>
#include <shell.h>
#include <vfs_posix.h>

#define TEST_TAG    "TEST"
#define DIR_NAME    "/dir_test"
#define FILE_NUM    3

static char *file_name[FILE_NUM] = {"test1.txt", "test2.txt", "test3.txt"};

static void dir_operation_sample(void)
{
    int i = 0;
    int fd;
    DIR *dirp;
    int save_offset = 0;
    struct dirent *dp;
    char bufread[32];
    char *str = "test";

    if(mkdir(DIR_NAME, 0x777) == -1)
    {
        LOG_E(TEST_TAG, "mkdir err");
        return;
    }
    else
    {
        LOG_W(TEST_TAG, "mkdir:%s", DIR_NAME);
    }

    if (chdir(DIR_NAME) != -1)
    {
        LOG_W(TEST_TAG, "chdir:%s", DIR_NAME);
    }
    getcwd(bufread, sizeof(bufread));
    LOG_W(TEST_TAG, "getcwd:%s", bufread);

    dirp = opendir(DIR_NAME);
    dp = readdir(dirp);
    if (dp == NULL)
    {
        LOG_W(TEST_TAG, "there is no dir entry");
    }
    closedir(dirp);

    for (i = 0; i < FILE_NUM; i++)
    {
        fd = open(file_name[i], O_RDWR | O_CREAT);
        if (fd != -1)
        {
            LOG_W(TEST_TAG, "open(create) file:%s", file_name[i]);            
            write(fd, str, strlen(str));
            close(fd);
        }
    }

    i = 0;    
    dirp = opendir(DIR_NAME);
    for (dp = readdir(dirp); dp != OS_NULL; dp = readdir(dirp))
    {
        i++;
        LOG_W(TEST_TAG, "readdir: %s (type:%d)", dp->d_name, dp->d_type);
        if (i == 2)
        {
            save_offset = telldir(dirp);
            LOG_W(TEST_TAG, "save the 2nd dir entry, it's offset:%d", save_offset);
        }
    }

    LOG_W(TEST_TAG, "seek to the 2nd dir entry, then readdir from it");
    seekdir(dirp, save_offset);
    for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
    {
        LOG_W(TEST_TAG, "readdir: %s (type:%d)", dp->d_name, dp->d_type);
    }
    closedir(dirp);

    for (i = 0; i < FILE_NUM; i++)
    {        
        LOG_W(TEST_TAG, "rm file:%s", file_name[i]);
        unlink(file_name[i]);
    }

    if (chdir("/") != -1)
    {
        LOG_W(TEST_TAG, "chdir:/");
    }
    getcwd(bufread, sizeof(bufread));
    LOG_W(TEST_TAG, "getcwd:%s", bufread);

    LOG_W(TEST_TAG, "rm dir:%s", DIR_NAME);
    unlink(DIR_NAME);
}

SH_CMD_EXPORT(test_dir, dir_operation_sample, "test for dir operation");

运行结果如下:

sh />test_dir
W/TEST: mkdir:/dir_test
W/TEST: chdir:/dir_test
W/TEST: getcwd:/dir_test
W/TEST: there is no dir entry
W/TEST: open(create) file:test1.txt
W/TEST: open(create) file:test2.txt
W/TEST: open(create) file:test3.txt
W/TEST: readdir: test1.txt (type:1)
W/TEST: readdir: test2.txt (type:1)
W/TEST: save the 2nd dir entry, it's offset:260
W/TEST: readdir: test3.txt (type:1)
W/TEST: seek to the 2nd dir entry, then readdir from it
W/TEST: readdir: test2.txt (type:1)
W/TEST: readdir: test3.txt (type:1)
W/TEST: rm file:test1.txt
W/TEST: rm file:test2.txt
W/TEST: rm file:test3.txt
W/TEST: chdir:/
W/TEST: getcwd:/
W/TEST: rm dir:/dir_test

文件操作shell命令使用示例

本例演示了常用shell命令的使用方法,包括ls/echo/cat/mkdir/pwd/rename/rm/cd等

sh />ls
Directory /:
sh />echo 111 test1.txt
sh />ls
Directory /:
test1.txt           3                        
sh />cat test1.txt
111
sh />mkdir dir_test
sh />ls
Directory /:
test1.txt           3                        
dir_test            <DIR>                    
sh />cd dir_test
sh /dir_test>echo 222 test2.txt
sh /dir_test>ls
Directory /dir_test:
test2.txt           3                        
sh /dir_test>cat test2.txt
222
sh /dir_test>pwd
/dir_test
sh /dir_test>mv test2.txt test3.txt
test2.txt => test3.txt
sh /dir_test>ls
Directory /dir_test:
test3.txt           3                        
sh /dir_test>cat test3.txt
222
sh /dir_test>rm test3.txt
sh /dir_test>ls
Directory /dir_test:
sh /dir_test>cd ..
sh />ls
Directory /:
test1.txt           3                        
dir_test            <DIR>                    
sh />rm dir_test
sh />ls
Directory /:
test1.txt           3                        
sh />

results matching ""

    No results matching ""

    返回顶部