From 6841521a776f18a7f6e6a0919adbe01b975d3943 Mon Sep 17 00:00:00 2001 From: bernard Date: Sun, 23 Aug 2026 07:43:50 +0800 Subject: [PATCH 1/9] [dfs][posix] Add record locks, fchmod, and tmpfs metadata MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为 DFS v2 增加 POSIX 记录锁、fchmod,以及 tmpfs 的权限和时间元数据。 进程退出时通过 dfs_fdtable_drop_fd 释放文件描述符和记录锁。 --- .../dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c | 276 ++++++--- .../dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.h | 20 +- .../dfs_v2/filesystems/tmpfs/utest/tmpfs.c | 76 ++- components/dfs/dfs_v2/include/dfs_file.h | 4 + components/dfs/dfs_v2/src/dfs.c | 14 +- components/dfs/dfs_v2/src/dfs_file.c | 40 +- components/dfs/dfs_v2/src/dfs_posix.c | 96 +++- components/dfs/dfs_v2/src/dfs_record_lock.c | 526 ++++++++++++++++++ components/dfs/dfs_v2/src/dfs_vnode.c | 2 + components/lwp/lwp_pid.c | 3 +- 10 files changed, 943 insertions(+), 114 deletions(-) create mode 100644 components/dfs/dfs_v2/src/dfs_record_lock.c diff --git a/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c b/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c index cc511415a3e2..0f1d42e9f860 100644 --- a/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c +++ b/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c @@ -25,8 +25,8 @@ #include "dfs_tmpfs.h" -#define DBG_TAG "tmpfs" -#define DBG_LVL DBG_INFO +#define DBG_TAG "tmpfs" +#define DBG_LVL DBG_INFO #include #ifdef RT_USING_PAGECACHE #include "dfs_pcache.h" @@ -36,20 +36,38 @@ static ssize_t dfs_tmp_page_read(struct dfs_file *file, struct dfs_page *page); static ssize_t dfs_tmp_page_write(struct dfs_page *page); -static struct dfs_aspace_ops dfs_tmp_aspace_ops = -{ +static struct dfs_aspace_ops dfs_tmp_aspace_ops = { .read = dfs_tmp_page_read, .write = dfs_tmp_page_write, }; #endif -static int _path_separate(const char *path, char *parent_path, char *file_name) +static int _tmpfs_path_validate(const char *path) +{ + if (path == RT_NULL || path[0] != '/') + { + return -EINVAL; + } + + return RT_EOK; +} + +static int _path_separate(const char *path, char *parent_path, rt_size_t parent_size, + char *file_name, rt_size_t file_size) { const char *path_p, *path_q; + rt_size_t parent_len, file_len; + int ret; - RT_ASSERT(path[0] == '/'); + ret = _tmpfs_path_validate(path); + if (ret != RT_EOK || parent_path == RT_NULL || file_name == RT_NULL || + parent_size < 2 || file_size == 0) + { + return ret != RT_EOK ? ret : -EINVAL; + } file_name[0] = '\0'; + parent_path[0] = '\0'; path_p = path_q = &path[1]; __next_dir: while (*path_q != '/' && *path_q != '\0') @@ -66,10 +84,17 @@ static int _path_separate(const char *path, char *parent_path, char *file_name) } else /* Last level dir */ { - rt_memcpy(parent_path, path, path_p - path - 1); - parent_path[path_p - path - 1] = '\0'; - rt_memcpy(file_name, path_p, path_q - path_p); - file_name[path_q - path_p] = '\0'; + parent_len = path_p - path - 1; + file_len = path_q - path_p; + if (parent_len >= parent_size || file_len >= file_size) + { + return -ENAMETOOLONG; + } + + rt_memcpy(parent_path, path, parent_len); + parent_path[parent_len] = '\0'; + rt_memcpy(file_name, path_p, file_len); + file_name[file_len] = '\0'; } } if (parent_path[0] == 0) @@ -83,18 +108,35 @@ static int _path_separate(const char *path, char *parent_path, char *file_name) return 0; } -static int _get_subdir(const char *path, char *name) +static int _get_subdir(const char *path, char *name, rt_size_t name_size) { const char *subpath = path; + rt_size_t name_len = 0; + + if (path == RT_NULL || name == RT_NULL || name_size == 0) + { + return -EINVAL; + } + while (*subpath == '/' && *subpath) - subpath ++; + { + subpath++; + } while (*subpath != '/' && *subpath) { + if (name_len + 1 >= name_size) + { + name[0] = '\0'; + return -ENAMETOOLONG; + } *name = *subpath; - name ++; - subpath ++; + name++; + subpath++; + name_len++; } - return 0; + *name = '\0'; + + return RT_EOK; } static int _free_subdir(struct tmpfs_file *dfile) @@ -143,6 +185,8 @@ static int dfs_tmpfs_mount(struct dfs_mnt *mnt, superblock->root.name[0] = '/'; superblock->root.sb = superblock; superblock->root.type = TMPFS_TYPE_DIR; + superblock->root.nlink = 1; + superblock->root.mode = S_IFDIR | (S_IRWXU | S_IRWXG | S_IRWXO); dfs_vfs_init_node(&superblock->root.node); rt_spin_lock_init(&superblock->lock); @@ -180,9 +224,9 @@ int dfs_tmpfs_statfs(struct dfs_mnt *mnt, struct statfs *buf) RT_ASSERT(superblock != NULL); RT_ASSERT(buf != NULL); - buf->f_bsize = 512; + buf->f_bsize = 512; buf->f_blocks = (superblock->df_size + 511) / 512; - buf->f_bfree = 1; + buf->f_bfree = 1; buf->f_bavail = buf->f_bfree; return RT_EOK; @@ -226,18 +270,28 @@ int dfs_tmpfs_ioctl(struct dfs_file *file, int cmd, void *args) return -EIO; } -struct tmpfs_file *dfs_tmpfs_lookup(struct tmpfs_sb *superblock, - const char *path, - rt_size_t *size) +struct tmpfs_file *dfs_tmpfs_lookup(struct tmpfs_sb *superblock, + const char *path, + rt_size_t *size) { const char *subpath, *curpath, *filename = RT_NULL; char subdir_name[TMPFS_NAME_MAX]; struct tmpfs_file *file, *curfile, *tmp; + int ret; + + ret = _tmpfs_path_validate(path); + if (ret != RT_EOK) + { + rt_set_errno(ret); + return RT_NULL; + } subpath = path; while (*subpath == '/' && *subpath) - subpath ++; - if (! *subpath) /* is root directory */ + { + subpath++; + } + if (!*subpath) /* is root directory */ { *size = 0; return &(superblock->root); @@ -248,15 +302,25 @@ struct tmpfs_file *dfs_tmpfs_lookup(struct tmpfs_sb *superblock, find_subpath: while (*subpath != '/' && *subpath) - subpath ++; + { + subpath++; + } - if (! *subpath) /* is last directory */ + if (!*subpath) /* is last directory */ + { filename = curpath; + } else - subpath ++; /* skip '/' */ + { + subpath++; /* skip '/' */ + } - memset(subdir_name, 0, TMPFS_NAME_MAX); - _get_subdir(curpath, subdir_name); + ret = _get_subdir(curpath, subdir_name, sizeof(subdir_name)); + if (ret != RT_EOK) + { + rt_set_errno(ret); + return RT_NULL; + } rt_spin_lock(&superblock->lock); @@ -297,12 +361,18 @@ static ssize_t dfs_tmpfs_read(struct dfs_file *file, void *buf, size_t count, of rt_mutex_take(&file->vnode->lock, RT_WAITING_FOREVER); ssize_t size = (ssize_t)file->vnode->size; if ((ssize_t)count < size - *pos) + { length = count; + } else + { length = size - *pos; + } if (length > 0) + { memcpy(buf, &(d_file->data[*pos]), length); + } /* update file current position */ *pos += length; @@ -341,7 +411,9 @@ static ssize_t _dfs_tmpfs_write(struct tmpfs_file *d_file, const void *buf, size } if (count > 0) + { memcpy(d_file->data + *pos, buf, count); + } /* update file current position */ *pos += count; @@ -399,12 +471,16 @@ static int dfs_tmpfs_close(struct dfs_file *file) RT_ASSERT(file->vnode->ref_count > 0); if (file->vnode->ref_count != 1) + { return 0; + } d_file = (struct tmpfs_file *)file->vnode->data; if (d_file == NULL) + { return -ENOENT; + } if (d_file->fre_memory == RT_TRUE) { @@ -454,7 +530,7 @@ static int dfs_tmpfs_open(struct dfs_file *file) } RT_ASSERT(file->vnode->ref_count > 0); - if(file->vnode->ref_count == 1) + if (file->vnode->ref_count == 1) { dfs_vnode_lock_init(file->vnode, file->dentry); } @@ -472,29 +548,59 @@ static int dfs_tmpfs_stat(struct dfs_dentry *dentry, struct stat *st) d_file = dfs_tmpfs_lookup(superblock, dentry->pathname, &size); if (d_file == NULL) + { return -ENOENT; + } st->st_dev = (dev_t)(size_t)(dentry->mnt->dev_id); st->st_ino = (ino_t)dfs_dentry_full_path_crc32(dentry); - if (d_file->type == TMPFS_TYPE_DIR) + st->st_mode = d_file->mode; + + st->st_size = d_file->size; + st->st_nlink = d_file->nlink; + st->st_mtime = 0; + + return RT_EOK; +} + +static int dfs_tmpfs_setattr(struct dfs_dentry *dentry, struct dfs_attr *attr) +{ + rt_size_t size; + struct tmpfs_file *d_file; + struct tmpfs_sb *superblock; + + if (dentry == RT_NULL || attr == RT_NULL || dentry->mnt == RT_NULL) { - st->st_mode = S_IFDIR | (S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + return -EINVAL; } - else + + superblock = (struct tmpfs_sb *)dentry->mnt->data; + d_file = dfs_tmpfs_lookup(superblock, dentry->pathname, &size); + if (d_file == RT_NULL) { - st->st_mode = S_IFREG | (S_IRWXU | S_IRWXG | S_IRWXO); + return -ENOENT; } - st->st_size = d_file->size; - st->st_mtime = 0; + if (attr->ia_valid & ATTR_MODE_SET) + { + mode_t permissions = attr->st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); + + rt_spin_lock(&superblock->lock); + d_file->mode = (d_file->mode & S_IFMT) | permissions; + if (dentry->vnode != RT_NULL) + { + dentry->vnode->mode = d_file->mode; + } + rt_spin_unlock(&superblock->lock); + } return RT_EOK; } static int dfs_tmpfs_getdents(struct dfs_file *file, - struct dirent *dirp, - uint32_t count) + struct dirent *dirp, + uint32_t count) { rt_size_t index, end; struct dirent *d; @@ -505,7 +611,7 @@ static int dfs_tmpfs_getdents(struct dfs_file *file, rt_mutex_take(&file->vnode->lock, RT_WAITING_FOREVER); - superblock = d_file->sb; + superblock = d_file->sb; RT_ASSERT(superblock != RT_NULL); RT_UNUSED(superblock); @@ -539,9 +645,10 @@ static int dfs_tmpfs_getdents(struct dfs_file *file, { d->d_type = DT_SOCK; } - d->d_namlen = RT_NAME_MAX; + d->d_namlen = rt_strlen(n_file->name); d->d_reclen = (rt_uint16_t)sizeof(struct dirent); - rt_strncpy(d->d_name, n_file->name, TMPFS_NAME_MAX); + rt_strncpy(d->d_name, n_file->name, DIRENT_NAME_MAX); + d->d_name[DIRENT_NAME_MAX - 1] = '\0'; count += 1; file->fpos += 1; @@ -568,9 +675,12 @@ static int dfs_tmpfs_unlink(struct dfs_dentry *dentry) d_file = dfs_tmpfs_lookup(superblock, dentry->pathname, &size); if (d_file == NULL) + { return -ENOENT; + } rt_spin_lock(&superblock->lock); + d_file->nlink = 0; dfs_vfs_remove_node(&d_file->node); rt_spin_unlock(&superblock->lock); @@ -599,18 +709,11 @@ static int dfs_tmpfs_rename(struct dfs_dentry *old_dentry, struct dfs_dentry *ne rt_size_t size; char *parent_path; char file_name[TMPFS_NAME_MAX]; + int ret; superblock = (struct tmpfs_sb *)old_dentry->mnt->data; RT_ASSERT(superblock != NULL); - d_file = dfs_tmpfs_lookup(superblock, new_dentry->pathname, &size); - if (d_file != NULL) - return -EEXIST; - - d_file = dfs_tmpfs_lookup(superblock, old_dentry->pathname, &size); - if (d_file == NULL) - return -ENOENT; - parent_path = rt_malloc(DFS_PATH_MAX); if (!parent_path) { @@ -618,21 +721,52 @@ static int dfs_tmpfs_rename(struct dfs_dentry *old_dentry, struct dfs_dentry *ne } /* find parent file */ - _path_separate(new_dentry->pathname, parent_path, file_name); + ret = _path_separate(new_dentry->pathname, parent_path, DFS_PATH_MAX, + file_name, sizeof(file_name)); + if (ret != RT_EOK) + { + rt_free(parent_path); + return ret; + } if (file_name[0] == '\0') /* it's root dir */ { rt_free(parent_path); return -ENOENT; } + + d_file = dfs_tmpfs_lookup(superblock, new_dentry->pathname, &size); + if (d_file != NULL) + { + rt_free(parent_path); + return -EEXIST; + } + + d_file = dfs_tmpfs_lookup(superblock, old_dentry->pathname, &size); + if (d_file == NULL) + { + rt_free(parent_path); + return -ENOENT; + } + /* open parent directory */ p_file = dfs_tmpfs_lookup(superblock, parent_path, &size); - RT_ASSERT(p_file != NULL); + if (p_file == RT_NULL) + { + rt_free(parent_path); + return -ENOENT; + } + if (p_file->type != TMPFS_TYPE_DIR) + { + rt_free(parent_path); + return -ENOTDIR; + } rt_spin_lock(&superblock->lock); dfs_vfs_remove_node(&d_file->node); rt_spin_unlock(&superblock->lock); - strncpy(d_file->name, file_name, TMPFS_NAME_MAX); + rt_strncpy(d_file->name, file_name, sizeof(d_file->name)); + d_file->name[sizeof(d_file->name) - 1] = '\0'; rt_spin_lock(&superblock->lock); dfs_vfs_append_node(&p_file->node, &d_file->node); @@ -665,17 +799,17 @@ static struct dfs_vnode *_dfs_tmpfs_lookup(struct dfs_dentry *dentry) { if (d_file->type == TMPFS_TYPE_DIR) { - vnode->mode = S_IFDIR | (S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + vnode->mode = d_file->mode; vnode->type = FT_DIRECTORY; } else if (d_file->type == TMPFS_TYPE_SOCKET) { - vnode->mode = S_IFSOCK | (S_IRWXU | S_IRWXG | S_IRWXO); + vnode->mode = d_file->mode; vnode->type = FT_SOCKET; } else { - vnode->mode = S_IFREG | (S_IRWXU | S_IRWXG | S_IRWXO); + vnode->mode = d_file->mode; vnode->type = FT_REGULAR; #ifdef RT_USING_PAGECACHE vnode->aspace = dfs_aspace_create(dentry, vnode, &dfs_tmp_aspace_ops); @@ -685,6 +819,7 @@ static struct dfs_vnode *_dfs_tmpfs_lookup(struct dfs_dentry *dentry) vnode->mnt = dentry->mnt; vnode->data = d_file; vnode->size = d_file->size; + vnode->nlink = d_file->nlink; } } @@ -699,6 +834,7 @@ static struct dfs_vnode *dfs_tmpfs_create_vnode(struct dfs_dentry *dentry, int t struct tmpfs_file *d_file, *p_file; char *parent_path; char file_name[TMPFS_NAME_MAX]; + int ret; if (dentry == NULL || dentry->mnt == NULL || dentry->mnt->data == NULL) { @@ -718,7 +854,15 @@ static struct dfs_vnode *dfs_tmpfs_create_vnode(struct dfs_dentry *dentry, int t if (vnode) { /* find parent file */ - _path_separate(dentry->pathname, parent_path, file_name); + ret = _path_separate(dentry->pathname, parent_path, DFS_PATH_MAX, + file_name, sizeof(file_name)); + if (ret != RT_EOK) + { + rt_set_errno(ret); + rt_free(parent_path); + dfs_vnode_destroy(vnode); + return RT_NULL; + } if (file_name[0] == '\0') /* it's root dir */ { rt_free(parent_path); @@ -746,30 +890,35 @@ static struct dfs_vnode *dfs_tmpfs_create_vnode(struct dfs_dentry *dentry, int t superblock->df_size += sizeof(struct tmpfs_file); - strncpy(d_file->name, file_name, TMPFS_NAME_MAX); + rt_strncpy(d_file->name, file_name, sizeof(d_file->name)); + d_file->name[sizeof(d_file->name) - 1] = '\0'; dfs_vfs_init_node(&d_file->node); d_file->data = NULL; d_file->size = 0; + d_file->nlink = 1; d_file->sb = superblock; d_file->fre_memory = RT_FALSE; if (type == FT_DIRECTORY) { d_file->type = TMPFS_TYPE_DIR; - vnode->mode = S_IFDIR | (S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); + d_file->mode = S_IFDIR | (mode & (S_IRWXU | S_IRWXG | S_IRWXO)); + vnode->mode = d_file->mode; vnode->type = FT_DIRECTORY; } else if (type == FT_SOCKET || (type == FT_REGULAR && S_ISSOCK(mode))) { d_file->type = TMPFS_TYPE_SOCKET; - vnode->mode = S_IFSOCK | (mode & (S_IRWXU | S_IRWXG | S_IRWXO)); + d_file->mode = S_IFSOCK | (mode & (S_IRWXU | S_IRWXG | S_IRWXO)); + vnode->mode = d_file->mode; vnode->type = FT_SOCKET; } else { d_file->type = TMPFS_TYPE_FILE; - vnode->mode = S_IFREG | (S_IRWXU | S_IRWXG | S_IRWXO); + d_file->mode = S_IFREG | (mode & (S_IRWXU | S_IRWXG | S_IRWXO)); + vnode->mode = d_file->mode; vnode->type = FT_REGULAR; #ifdef RT_USING_PAGECACHE vnode->aspace = dfs_aspace_create(dentry, vnode, &dfs_tmp_aspace_ops); @@ -782,6 +931,7 @@ static struct dfs_vnode *dfs_tmpfs_create_vnode(struct dfs_dentry *dentry, int t vnode->mnt = dentry->mnt; vnode->data = d_file; vnode->size = d_file->size; + vnode->nlink = d_file->nlink; } rt_free(parent_path); @@ -872,8 +1022,7 @@ static int dfs_tmpfs_truncate(struct dfs_file *file, off_t offset) return 0; } -static const struct dfs_file_ops _tmp_fops = -{ +static const struct dfs_file_ops _tmp_fops = { .open = dfs_tmpfs_open, .close = dfs_tmpfs_close, .ioctl = dfs_tmpfs_ioctl, @@ -884,8 +1033,7 @@ static const struct dfs_file_ops _tmp_fops = .truncate = dfs_tmpfs_truncate, }; -static const struct dfs_filesystem_ops _tmpfs_ops = -{ +static const struct dfs_filesystem_ops _tmpfs_ops = { .name = "tmp", .flags = DFS_FS_FLAG_DEFAULT, .default_fops = &_tmp_fops, @@ -896,14 +1044,14 @@ static const struct dfs_filesystem_ops _tmpfs_ops = .unlink = dfs_tmpfs_unlink, .stat = dfs_tmpfs_stat, + .setattr = dfs_tmpfs_setattr, .rename = dfs_tmpfs_rename, .lookup = _dfs_tmpfs_lookup, .create_vnode = dfs_tmpfs_create_vnode, .free_vnode = dfs_tmpfs_free_vnode }; -static struct dfs_filesystem_type _tmpfs = -{ +static struct dfs_filesystem_type _tmpfs = { .fs_ops = &_tmpfs_ops, }; diff --git a/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.h b/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.h index 0807f06d19bc..ee3124f69e0d 100644 --- a/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.h +++ b/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.h @@ -14,8 +14,8 @@ #include #include -#define TMPFS_NAME_MAX 32 -#define TMPFS_MAGIC 0x0B0B0B0B +#define TMPFS_NAME_MAX DIRENT_NAME_MAX +#define TMPFS_MAGIC 0x0B0B0B0B #define TMPFS_TYPE_FILE 0x00 #define TMPFS_TYPE_DIR 0x01 @@ -25,22 +25,24 @@ struct tmpfs_sb; struct tmpfs_file { - rt_uint32_t type; /* file type */ + rt_uint32_t type; /* file type */ char name[TMPFS_NAME_MAX]; /* file name */ struct dfs_vfs_node node; /* file node in the tmpfs */ struct tmpfs_sb *sb; /* superblock ptr */ - rt_uint8_t *data; /* file date ptr */ - rt_size_t size; /* file size */ - rt_bool_t fre_memory;/* Whether to release memory upon close */ + rt_uint8_t *data; /* file date ptr */ + rt_size_t size; /* file size */ + rt_uint32_t nlink; /* hard link count */ + mode_t mode; /* file type and permission bits */ + rt_bool_t fre_memory;/* Whether to release memory upon close */ }; struct tmpfs_sb { - rt_uint32_t magic; /* TMPFS_MAGIC */ + rt_uint32_t magic; /* TMPFS_MAGIC */ struct tmpfs_file root; /* root dir */ - rt_size_t df_size; /* df size */ - rt_list_t sibling; /* sb sibling list */ + rt_size_t df_size; /* df size */ + rt_list_t sibling; /* sb sibling list */ struct rt_spinlock lock; /* tmpfs lock */ }; diff --git a/components/dfs/dfs_v2/filesystems/tmpfs/utest/tmpfs.c b/components/dfs/dfs_v2/filesystems/tmpfs/utest/tmpfs.c index 647944a71471..502c472718c8 100644 --- a/components/dfs/dfs_v2/filesystems/tmpfs/utest/tmpfs.c +++ b/components/dfs/dfs_v2/filesystems/tmpfs/utest/tmpfs.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -20,7 +21,7 @@ void run_copy() { int ret = 0; - ret = msh_exec("cd /tmp", 7); + ret = msh_exec("cd /tmp", 7); if (ret != 0) { LOG_E("errno=%d, ret=%d\n", errno, ret); @@ -56,17 +57,76 @@ void run_copy() } +static void run_long_name(void) +{ + static const char source[] = + "/tmp/iceoryx2_node_1234567890_abcdefghijklmnopqrstuvwxyz_shared"; + static const char destination[] = + "/tmp/iceoryx2_node_abcdefghijklmnopqrstuvwxyz_0987654321_renamed"; + static const char payload[] = "tmpfs-long-name"; + char buffer[sizeof(payload)] = { 0 }; + struct stat st = { 0 }; + int fd; + + fd = open(source, O_CREAT | O_RDWR | O_TRUNC, 0); + uassert_true(fd >= 0); + uassert_int_equal(fstat(fd, &st), 0); + uassert_int_equal(st.st_nlink, 1); + uassert_int_equal(write(fd, payload, sizeof(payload)), sizeof(payload)); + uassert_int_equal(close(fd), 0); + + uassert_int_equal(rename(source, destination), 0); + fd = open(destination, O_RDONLY, 0); + uassert_true(fd >= 0); + uassert_int_equal(read(fd, buffer, sizeof(buffer)), sizeof(buffer)); + uassert_str_equal(buffer, payload); + uassert_int_equal(close(fd), 0); + uassert_int_equal(unlink(destination), 0); +} -static void run_long_name_reject(void) +static void run_too_long_name_reject(void) { - static const char long_name[] = - "/tmp/abcdefghijklmnopqrstuvwxyz1234567890"; + static const char prefix[] = "/tmp/"; + char path[(sizeof(prefix) - 1) + DIRENT_NAME_MAX + 1]; int fd; - errno = 0; - fd = open(long_name, O_CREAT | O_RDWR, 0); + rt_memcpy(path, prefix, sizeof(prefix) - 1); + rt_memset(path + sizeof(prefix) - 1, 'x', DIRENT_NAME_MAX); + path[sizeof(path) - 1] = '\0'; + + fd = open(path, O_CREAT | O_RDWR, 0); uassert_int_equal(fd, -1); + + fd = open("/tmp/tmpfs-after-long-name", O_CREAT | O_RDWR | O_TRUNC, 0); + uassert_true(fd >= 0); + uassert_int_equal(close(fd), 0); + + errno = 0; + uassert_int_equal(rename("/tmp/tmpfs-after-long-name", path), -1); uassert_int_equal(errno, -ENAMETOOLONG); + uassert_int_equal(unlink("/tmp/tmpfs-after-long-name"), 0); +} + +static void run_mode_change(void) +{ + static const char path[] = "/tmp/tmpfs-mode-change"; + struct stat st = { 0 }; + int fd; + + unlink(path); + fd = open(path, O_CREAT | O_EXCL | O_RDWR, 0700); + uassert_true(fd >= 0); + uassert_int_equal(fstat(fd, &st), 0); + uassert_int_equal(st.st_mode & 0777, 0700); + uassert_int_equal(fchmod(fd, 0400), 0); + uassert_int_equal(fstat(fd, &st), 0); + uassert_int_equal(st.st_mode & 0777, 0400); + uassert_int_equal(close(fd), 0); + + errno = 0; + uassert_int_equal(open(path, O_WRONLY, 0), -1); + uassert_int_equal(errno, -EACCES); + uassert_int_equal(unlink(path), 0); } static rt_err_t utest_tc_init(void) @@ -81,6 +141,8 @@ static rt_err_t utest_tc_cleanup(void) static void testcase(void) { UTEST_UNIT_RUN(run_copy); - UTEST_UNIT_RUN(run_long_name_reject); + UTEST_UNIT_RUN(run_long_name); + UTEST_UNIT_RUN(run_too_long_name_reject); + UTEST_UNIT_RUN(run_mode_change); } UTEST_TC_EXPORT(testcase, "testcase.tfs.tmpfs", utest_tc_init, utest_tc_cleanup, 10); diff --git a/components/dfs/dfs_v2/include/dfs_file.h b/components/dfs/dfs_v2/include/dfs_file.h index 280d0dbcb9de..60401f7ef05b 100644 --- a/components/dfs/dfs_v2/include/dfs_file.h +++ b/components/dfs/dfs_v2/include/dfs_file.h @@ -163,9 +163,13 @@ off_t dfs_file_lseek(struct dfs_file *file, off_t offset, int wherece); int dfs_file_stat(const char *path, struct stat *buf); int dfs_file_lstat(const char *path, struct stat *buf); int dfs_file_setattr(const char *path, struct dfs_attr *attr); +int dfs_file_fsetattr(struct dfs_file *file, struct dfs_attr *attr); int dfs_file_fstat(struct dfs_file *file, struct stat *buf); int dfs_file_ioctl(struct dfs_file *file, int cmd, void *args); int dfs_file_fcntl(int fd, int cmd, unsigned long arg); +int dfs_record_lock_init(void); +int dfs_record_lock_fcntl(struct dfs_file *file, int cmd, struct flock *flock); +void dfs_record_lock_release(struct dfs_file *file, struct dfs_fdtable *owner); int dfs_file_fsync(struct dfs_file *file); int dfs_file_unlink(const char *path); int dfs_file_link(const char *oldname, const char *newname); diff --git a/components/dfs/dfs_v2/src/dfs.c b/components/dfs/dfs_v2/src/dfs.c index 92c688a8a070..f1601f000389 100644 --- a/components/dfs/dfs_v2/src/dfs.c +++ b/components/dfs/dfs_v2/src/dfs.c @@ -236,6 +236,7 @@ int dfs_init(void) /* create device filesystem lock */ rt_mutex_init(&fslock, "fslock", RT_IPC_FLAG_FIFO); rt_mutex_init(&fdlock, "fdlock", RT_IPC_FLAG_FIFO); + dfs_record_lock_init(); /* Initialize dentry system */ dfs_dentry_init(); @@ -740,6 +741,7 @@ int dfs_fdtable_dup(struct dfs_fdtable *fdt_dst, struct dfs_fdtable *fdt_src, in int dfs_fdtable_drop_fd(struct dfs_fdtable *fdt, int fd) { int err = 0; + struct dfs_file *file; if (fdt == NULL) { @@ -751,7 +753,15 @@ int dfs_fdtable_drop_fd(struct dfs_fdtable *fdt, int fd) return -RT_ENOSYS; } - err = dfs_file_close(fdt->fds[fd]); + file = fdt_get_file(fdt, fd); + if (file == RT_NULL) + { + dfs_file_unlock(); + return -EBADF; + } + + dfs_record_lock_release(file, fdt); + err = dfs_file_close(file); if (!err) { fdt_fd_release(fdt, fd); @@ -901,6 +911,7 @@ int dfs_dup_from(int oldfd, struct dfs_fdtable *fdtab) file->data = fdtab->fds[oldfd]->data; } + dfs_record_lock_release(fdtab->fds[oldfd], fdtab); dfs_file_close(fdtab->fds[oldfd]); exit: @@ -990,6 +1001,7 @@ rt_err_t sys_dup2(int oldfd, int newfd) if (fdt->fds[newfd]) { + dfs_record_lock_release(fdt->fds[newfd], fdt); ret = dfs_file_close(fdt->fds[newfd]); if (ret < 0) { diff --git a/components/dfs/dfs_v2/src/dfs_file.c b/components/dfs/dfs_v2/src/dfs_file.c index 6b1615939677..970b74c6caee 100644 --- a/components/dfs/dfs_v2/src/dfs_file.c +++ b/components/dfs/dfs_v2/src/dfs_file.c @@ -558,7 +558,7 @@ char *dfs_file_realpath(struct dfs_mnt **mnt, const char *fullpath, int mode) * @return 0 on successful, -1 on failure: * -ENOENT if file doesn't exist and O_CREAT not set * -EEXIST if file exists and O_EXCL|O_CREAT set - * -EPERM if permission denied + * -EACCES if permission denied * -ENOTDIR if path is not a directory when O_DIRECTORY set * -EISDIR if path is directory when opening as regular file */ @@ -763,7 +763,7 @@ int dfs_file_open(struct dfs_file *file, const char *path, int oflags, mode_t mo { DLOG(msg, "dfs_file", mnt->fs_ops->name, DLOG_MSG, "no permission or fops->open"); dfs_file_unref(file); - ret = -EPERM; + ret = -EACCES; } } else @@ -1401,6 +1401,10 @@ int dfs_file_stat(const char *path, struct stat *buf) if (dfs_is_mounted(mnt) == 0) { ret = mnt->fs_ops->stat(dentry, buf); + if (ret == RT_EOK && buf->st_nlink == 0 && dentry->vnode != RT_NULL) + { + buf->st_nlink = dentry->vnode->nlink; + } } } @@ -1470,6 +1474,10 @@ int dfs_file_lstat(const char *path, struct stat *buf) if (dfs_is_mounted(mnt) == 0) { ret = mnt->fs_ops->stat(dentry, buf); + if (ret == RT_EOK && buf->st_nlink == 0 && dentry->vnode != RT_NULL) + { + buf->st_nlink = dentry->vnode->nlink; + } } } @@ -1596,6 +1604,29 @@ int dfs_file_setattr(const char *path, struct dfs_attr *attr) return ret; } +int dfs_file_fsetattr(struct dfs_file *file, struct dfs_attr *attr) +{ + struct dfs_mnt *mnt; + + if (file == RT_NULL || file->dentry == RT_NULL || attr == RT_NULL) + { + return -EBADF; + } + + mnt = file->dentry->mnt; + if (mnt == RT_NULL || mnt->fs_ops == RT_NULL || + mnt->fs_ops->setattr == RT_NULL) + { + return -ENOSYS; + } + if (dfs_is_mounted(mnt) != 0) + { + return -EINVAL; + } + + return mnt->fs_ops->setattr(file->dentry, attr); +} + /** * @brief Perform device-specific control operations * @@ -1654,7 +1685,7 @@ int dfs_file_ioctl(struct dfs_file *file, int cmd, void *args) * - F_SETFD: Set file descriptor flags * - F_GETFL: Get file status flags * - F_SETFL: Set file status flags - * - F_GETLK/F_SETLK/F_SETLKW: File locking operations (unimplemented) + * - F_GETLK/F_SETLK/F_SETLKW: POSIX advisory record locking * - F_DUPFD_CLOEXEC: Duplicate file descriptor with close-on-exec flag (if supported) * * @param[in] fd File descriptor to operate on @@ -1670,7 +1701,6 @@ int dfs_file_ioctl(struct dfs_file *file, int cmd, void *args) * -EPERM for unsupported commands * * @note Not all commands may be supported by all filesystems - * @note File locking operations (F_GETLK/F_SETLK/F_SETLKW) are currently unimplemented */ int dfs_file_fcntl(int fd, int cmd, unsigned long arg) { @@ -1724,9 +1754,11 @@ int dfs_file_fcntl(int fd, int cmd, unsigned long arg) break; } case F_GETLK: + ret = dfs_record_lock_fcntl(file, cmd, (struct flock *)arg); break; case F_SETLK: case F_SETLKW: + ret = dfs_record_lock_fcntl(file, cmd, (struct flock *)arg); break; #ifdef RT_USING_MUSLLIBC case F_DUPFD_CLOEXEC: diff --git a/components/dfs/dfs_v2/src/dfs_posix.c b/components/dfs/dfs_v2/src/dfs_posix.c index 2ca7b66f7e82..77410c4dd55e 100644 --- a/components/dfs/dfs_v2/src/dfs_posix.c +++ b/components/dfs/dfs_v2/src/dfs_posix.c @@ -119,7 +119,7 @@ int openat(int dirfd, const char *path, int flag, ...) return -1; } - fullpath = (char*)path; + fullpath = (char *)path; if (path[0] != '/') { @@ -176,7 +176,7 @@ int utimensat(int __fd, const char *__path, const struct timespec __times[2], in } else { - fullpath = (char*)__path; + fullpath = (char *)__path; } } else @@ -291,17 +291,8 @@ RTM_EXPORT(creat); int close(int fd) { int result; - struct dfs_file *file; - - file = fd_get(fd); - if (file == NULL) - { - rt_set_errno(-EBADF); - - return -1; - } - result = dfs_file_close(file); + result = dfs_fdtable_drop_fd(dfs_fdtable_get(), fd); if (result < 0) { rt_set_errno(result); @@ -309,8 +300,6 @@ int close(int fd) return -1; } - fd_release(fd); - return 0; } RTM_EXPORT(close); @@ -335,17 +324,22 @@ ssize_t read(int fd, void *buf, size_t len) ssize_t result; struct dfs_file *file; - if (buf == NULL) + file = fd_get(fd); + if (file == NULL) { rt_set_errno(-EBADF); + return -1; } - file = fd_get(fd); - if (file == NULL) + if (len == 0) { - rt_set_errno(-EBADF); + return 0; + } + if (buf == NULL) + { + rt_set_errno(-EBADF); return -1; } @@ -380,17 +374,22 @@ ssize_t write(int fd, const void *buf, size_t len) ssize_t result; struct dfs_file *file; - if (buf == NULL) + file = fd_get(fd); + if (file == NULL) { rt_set_errno(-EBADF); + return -1; } - file = fd_get(fd); - if (file == NULL) + if (len == 0) { - rt_set_errno(-EBADF); + return 0; + } + if (buf == NULL) + { + rt_set_errno(-EBADF); return -1; } @@ -581,6 +580,10 @@ int fstat(int fildes, struct stat *buf) if (dfs_is_mounted(file->dentry->mnt) == 0) { ret = file->dentry->mnt->fs_ops->stat(file->dentry, buf); + if (ret == RT_EOK && buf->st_nlink == 0 && file->vnode != RT_NULL) + { + buf->st_nlink = file->vnode->nlink; + } } return ret; @@ -655,11 +658,18 @@ int fcntl(int fildes, int cmd, ...) arg = va_arg(ap, void *); va_end(ap); - ret = dfs_file_ioctl(file, cmd, arg); - if (ret < 0) + if (cmd == F_GETLK || cmd == F_SETLK || cmd == F_SETLKW) { ret = dfs_file_fcntl(fildes, cmd, (unsigned long)arg); } + else + { + ret = dfs_file_ioctl(file, cmd, arg); + if (ret < 0) + { + ret = dfs_file_fcntl(fildes, cmd, (unsigned long)arg); + } + } } else { @@ -676,6 +686,32 @@ int fcntl(int fildes, int cmd, ...) } RTM_EXPORT(fcntl); +int fchmod(int fildes, mode_t mode) +{ + int ret; + struct dfs_attr attr = { 0 }; + struct dfs_file *file; + + file = fd_get(fildes); + if (file == RT_NULL) + { + rt_set_errno(-EBADF); + return -1; + } + + attr.st_mode = mode; + attr.ia_valid = ATTR_MODE_SET; + ret = dfs_file_fsetattr(file, &attr); + if (ret < 0) + { + rt_set_errno(ret); + return -1; + } + + return 0; +} +RTM_EXPORT(fchmod); + /** * this function is a POSIX compliant version, which shall perform a variety of * control functions on devices. @@ -892,7 +928,9 @@ int rmdir(const char *pathname) { dirent = readdir(dir); if (dirent == RT_NULL) + { break; + } if (rt_strcmp(".", dirent->d_name) != 0 && rt_strcmp("..", dirent->d_name) != 0) { @@ -964,7 +1002,7 @@ DIR *opendir(const char *name) if (result >= 0) { /* open successfully */ - t = (DIR *) rt_malloc(sizeof(DIR)); + t = (DIR *)rt_malloc(sizeof(DIR)); if (t == NULL) { dfs_file_close(file); @@ -1109,10 +1147,12 @@ void seekdir(DIR *d, long offset) { /* seek to the offset position of directory */ if (dfs_file_lseek(fd_get(d->fd), 0, SEEK_SET) >= 0) + { d->num = d->cur = 0; + } } - while(file->fpos < offset) + while (file->fpos < offset) { if (!readdir(d)) { @@ -1135,7 +1175,9 @@ void rewinddir(DIR *d) { /* seek to the beginning of directory */ if (dfs_file_lseek(fd_get(d->fd), 0, SEEK_SET) >= 0) + { d->num = d->cur = 0; + } } } RTM_EXPORT(rewinddir); @@ -1335,7 +1377,7 @@ void setcwd(char *buf) rt_kprintf(NO_WORKING_DIR); #endif - return ; + return; } RTM_EXPORT(setcwd); diff --git a/components/dfs/dfs_v2/src/dfs_record_lock.c b/components/dfs/dfs_v2/src/dfs_record_lock.c new file mode 100644 index 000000000000..1d159c025736 --- /dev/null +++ b/components/dfs/dfs_v2/src/dfs_record_lock.c @@ -0,0 +1,526 @@ +/* + * Copyright (c) 2006-2026 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include +#include +#include + +#ifdef RT_USING_SMART +#include +#endif + +struct dfs_lock_range +{ + off_t start; + off_t end; + rt_bool_t to_eof; +}; + +struct dfs_record_lock +{ + rt_list_t list; + struct dfs_vnode *vnode; + struct dfs_fdtable *owner; + pid_t pid; + short type; + struct dfs_lock_range range; +}; + +static rt_list_t _record_locks; +static struct rt_semaphore _record_lock_changed; +static int _record_lock_waiters; + +static off_t _off_max(void) +{ + return sizeof(off_t) == sizeof(rt_int64_t) ? (off_t)INT64_MAX : (off_t)INT32_MAX; +} + +static off_t _off_min(void) +{ + off_t max = _off_max(); + + return -max - 1; +} + +static int _add_offset(off_t left, off_t right, off_t *result) +{ + off_t max = _off_max(); + off_t min = _off_min(); + + if ((right > 0 && left > max - right) || + (right < 0 && left < min - right)) + { + return -EOVERFLOW; + } + + *result = left + right; + return 0; +} + +static int _normalize_range(struct dfs_file *file, const struct flock *flock, + struct dfs_lock_range *range) +{ + off_t base; + off_t origin; + off_t end; + int ret; + + switch (flock->l_whence) + { + case SEEK_SET: + base = 0; + break; + case SEEK_CUR: + base = dfs_file_get_fpos(file); + break; + case SEEK_END: + if (file->vnode->size > (size_t)_off_max()) + { + return -EOVERFLOW; + } + base = (off_t)file->vnode->size; + break; + default: + return -EINVAL; + } + + ret = _add_offset(base, flock->l_start, &origin); + if (ret < 0 || origin < 0) + { + return ret < 0 ? ret : -EINVAL; + } + + range->to_eof = RT_FALSE; + if (flock->l_len == 0) + { + range->start = origin; + range->end = 0; + range->to_eof = RT_TRUE; + return 0; + } + + if (flock->l_len > 0) + { + ret = _add_offset(origin, flock->l_len - 1, &end); + if (ret < 0) + { + return ret; + } + range->start = origin; + range->end = end; + } + else + { + ret = _add_offset(origin, flock->l_len, &range->start); + if (ret < 0 || range->start < 0 || origin == 0) + { + return ret < 0 ? ret : -EINVAL; + } + range->end = origin - 1; + } + + if (range->end == _off_max()) + { + range->end = 0; + range->to_eof = RT_TRUE; + } + return 0; +} + +static rt_bool_t _ranges_overlap(const struct dfs_lock_range *left, + const struct dfs_lock_range *right) +{ + if (!left->to_eof && left->end < right->start) + { + return RT_FALSE; + } + if (!right->to_eof && right->end < left->start) + { + return RT_FALSE; + } + return RT_TRUE; +} + +static rt_bool_t _ranges_touch(const struct dfs_lock_range *left, + const struct dfs_lock_range *right) +{ + if (_ranges_overlap(left, right)) + { + return RT_TRUE; + } + if (!left->to_eof && left->end < _off_max() && + left->end + 1 == right->start) + { + return RT_TRUE; + } + if (!right->to_eof && right->end < _off_max() && + right->end + 1 == left->start) + { + return RT_TRUE; + } + return RT_FALSE; +} + +static rt_bool_t _locks_conflict(short requested_type, + const struct dfs_record_lock *lock, + const struct dfs_lock_range *range, + struct dfs_fdtable *owner) +{ + if (lock->owner == owner || !_ranges_overlap(&lock->range, range)) + { + return RT_FALSE; + } + return requested_type == F_WRLCK || lock->type == F_WRLCK; +} + +static struct dfs_record_lock *_find_conflict( + struct dfs_vnode *vnode, struct dfs_fdtable *owner, short type, + const struct dfs_lock_range *range) +{ + rt_list_t *node; + struct dfs_record_lock *result = RT_NULL; + + rt_list_for_each(node, &_record_locks) + { + struct dfs_record_lock *lock; + + lock = rt_list_entry(node, struct dfs_record_lock, list); + if (lock->vnode == vnode && + _locks_conflict(type, lock, range, owner) && + (result == RT_NULL || lock->range.start < result->range.start)) + { + result = lock; + } + } + return result; +} + +static struct dfs_record_lock *_split_lock_needed( + struct dfs_vnode *vnode, struct dfs_fdtable *owner, + const struct dfs_lock_range *range) +{ + rt_list_t *node; + + if (range->to_eof) + { + return RT_NULL; + } + + rt_list_for_each(node, &_record_locks) + { + struct dfs_record_lock *lock; + + lock = rt_list_entry(node, struct dfs_record_lock, list); + if (lock->vnode == vnode && lock->owner == owner && + lock->range.start < range->start && + (lock->range.to_eof || lock->range.end > range->end)) + { + return lock; + } + } + return RT_NULL; +} + +static int _remove_owner_range(struct dfs_vnode *vnode, + struct dfs_fdtable *owner, + const struct dfs_lock_range *range) +{ + struct dfs_record_lock *split_source; + struct dfs_record_lock *split = RT_NULL; + rt_list_t *node; + rt_list_t *next; + + split_source = _split_lock_needed(vnode, owner, range); + if (split_source != RT_NULL) + { + split = rt_malloc(sizeof(*split)); + if (split == RT_NULL) + { + return -ENOLCK; + } + *split = *split_source; + rt_list_init(&split->list); + split->range.start = range->end + 1; + rt_list_insert_after(&split_source->list, &split->list); + } + + rt_list_for_each_safe(node, next, &_record_locks) + { + struct dfs_record_lock *lock; + + lock = rt_list_entry(node, struct dfs_record_lock, list); + if (lock == split || lock->vnode != vnode || lock->owner != owner || + !_ranges_overlap(&lock->range, range)) + { + continue; + } + + if (lock->range.start < range->start) + { + lock->range.end = range->start - 1; + lock->range.to_eof = RT_FALSE; + } + else if (!range->to_eof && + (lock->range.to_eof || lock->range.end > range->end)) + { + lock->range.start = range->end + 1; + } + else + { + rt_list_remove(&lock->list); + rt_free(lock); + } + } + return 0; +} + +static void _merge_owner_locks(struct dfs_record_lock *target) +{ + rt_list_t *node; + rt_list_t *next; + rt_bool_t merged; + + do + { + merged = RT_FALSE; + rt_list_for_each_safe(node, next, &_record_locks) + { + struct dfs_record_lock *lock; + + lock = rt_list_entry(node, struct dfs_record_lock, list); + if (lock == target || lock->vnode != target->vnode || + lock->owner != target->owner || lock->type != target->type || + !_ranges_touch(&lock->range, &target->range)) + { + continue; + } + + if (lock->range.start < target->range.start) + { + target->range.start = lock->range.start; + } + if (lock->range.to_eof || target->range.to_eof) + { + target->range.to_eof = RT_TRUE; + target->range.end = 0; + } + else if (lock->range.end > target->range.end) + { + target->range.end = lock->range.end; + } + rt_list_remove(&lock->list); + rt_free(lock); + merged = RT_TRUE; + } + } while (merged); +} + +static void _wake_waiter(void) +{ + if (_record_lock_waiters > 0) + { + rt_sem_release(&_record_lock_changed); + } +} + +static pid_t _owner_pid(struct dfs_fdtable *owner) +{ +#ifdef RT_USING_SMART + if (owner != RT_NULL && owner != dfs_fdtable_get_global()) + { + struct rt_lwp *lwp; + + lwp = rt_container_of(owner, struct rt_lwp, fdt); + return lwp->pid; + } +#endif + return 0; +} + +static int _set_lock(struct dfs_file *file, int cmd, struct flock *flock, + const struct dfs_lock_range *range) +{ + struct dfs_fdtable *owner = dfs_fdtable_get(); + struct dfs_record_lock *new_lock = RT_NULL; + rt_bool_t waiting = RT_FALSE; + int ret; + + if (flock->l_type != F_UNLCK) + { + new_lock = rt_malloc(sizeof(*new_lock)); + if (new_lock == RT_NULL) + { + return -ENOLCK; + } + new_lock->vnode = file->vnode; + new_lock->owner = owner; + new_lock->pid = _owner_pid(owner); + new_lock->type = flock->l_type; + new_lock->range = *range; + rt_list_init(&new_lock->list); + } + + for (;;) + { + if (dfs_file_lock() != RT_EOK) + { + ret = -ENOSYS; + break; + } + + if (flock->l_type != F_UNLCK && + _find_conflict(file->vnode, owner, flock->l_type, range) != RT_NULL) + { + if (cmd == F_SETLK) + { + dfs_file_unlock(); + ret = -EAGAIN; + break; + } + if (!waiting) + { + _record_lock_waiters++; + waiting = RT_TRUE; + } + dfs_file_unlock(); + if (rt_sem_take_interruptible(&_record_lock_changed, + RT_WAITING_FOREVER) != RT_EOK) + { + ret = -EINTR; + break; + } + continue; + } + + ret = _remove_owner_range(file->vnode, owner, range); + if (ret == 0 && new_lock != RT_NULL) + { + rt_list_insert_before(&_record_locks, &new_lock->list); + _merge_owner_locks(new_lock); + new_lock = RT_NULL; + } + if (waiting) + { + _record_lock_waiters--; + waiting = RT_FALSE; + } + if (ret == 0) + { + _wake_waiter(); + } + dfs_file_unlock(); + break; + } + + if (waiting && dfs_file_lock() == RT_EOK) + { + _record_lock_waiters--; + dfs_file_unlock(); + } + if (new_lock != RT_NULL) + { + rt_free(new_lock); + } + return ret; +} + +int dfs_record_lock_init(void) +{ + rt_list_init(&_record_locks); + _record_lock_waiters = 0; + return rt_sem_init(&_record_lock_changed, "freclck", 0, + RT_IPC_FLAG_FIFO); +} + +int dfs_record_lock_fcntl(struct dfs_file *file, int cmd, struct flock *flock) +{ + struct dfs_lock_range range; + struct dfs_record_lock *conflict; + struct dfs_fdtable *owner; + int access; + int ret; + + if (file == RT_NULL || file->vnode == RT_NULL || flock == RT_NULL) + { + return -EBADF; + } + if (flock->l_type != F_RDLCK && flock->l_type != F_WRLCK && + (flock->l_type != F_UNLCK || cmd == F_GETLK)) + { + return -EINVAL; + } + + access = dfs_fflags(file->flags); + if ((flock->l_type == F_RDLCK && !(access & DFS_F_FREAD)) || + (flock->l_type == F_WRLCK && !(access & DFS_F_FWRITE))) + { + return -EBADF; + } + + ret = _normalize_range(file, flock, &range); + if (ret < 0) + { + return ret; + } + if (cmd != F_GETLK) + { + return _set_lock(file, cmd, flock, &range); + } + + owner = dfs_fdtable_get(); + if (dfs_file_lock() != RT_EOK) + { + return -ENOSYS; + } + conflict = _find_conflict(file->vnode, owner, flock->l_type, &range); + if (conflict == RT_NULL) + { + flock->l_type = F_UNLCK; + } + else + { + flock->l_type = conflict->type; + flock->l_whence = SEEK_SET; + flock->l_start = conflict->range.start; + flock->l_len = conflict->range.to_eof ? 0 : conflict->range.end - conflict->range.start + 1; + flock->l_pid = conflict->pid; + } + dfs_file_unlock(); + return 0; +} + +void dfs_record_lock_release(struct dfs_file *file, struct dfs_fdtable *owner) +{ + rt_list_t *node; + rt_list_t *next; + rt_bool_t changed = RT_FALSE; + + if (file == RT_NULL || file->vnode == RT_NULL || owner == RT_NULL || + dfs_file_lock() != RT_EOK) + { + return; + } + + rt_list_for_each_safe(node, next, &_record_locks) + { + struct dfs_record_lock *lock; + + lock = rt_list_entry(node, struct dfs_record_lock, list); + if (lock->vnode == file->vnode && lock->owner == owner) + { + rt_list_remove(&lock->list); + rt_free(lock); + changed = RT_TRUE; + } + } + if (changed) + { + _wake_waiter(); + } + dfs_file_unlock(); +} diff --git a/components/dfs/dfs_v2/src/dfs_vnode.c b/components/dfs/dfs_v2/src/dfs_vnode.c index 462f94e05e68..0b133c1d2395 100644 --- a/components/dfs/dfs_v2/src/dfs_vnode.c +++ b/components/dfs/dfs_v2/src/dfs_vnode.c @@ -36,6 +36,7 @@ int dfs_vnode_init(struct dfs_vnode *vnode, int type, const struct dfs_file_ops vnode->type = type; rt_atomic_store(&(vnode->ref_count), 1); + vnode->nlink = 1; vnode->mnt = RT_NULL; vnode->fops = fops; } @@ -58,6 +59,7 @@ struct dfs_vnode *dfs_vnode_create(void) } rt_atomic_store(&(vnode->ref_count), 1); + vnode->nlink = 1; LOG_I("create a vnode: %p", vnode); diff --git a/components/lwp/lwp_pid.c b/components/lwp/lwp_pid.c index 82f205a694e0..12a4dde86f0a 100644 --- a/components/lwp/lwp_pid.c +++ b/components/lwp/lwp_pid.c @@ -413,8 +413,7 @@ static void __exit_files(struct rt_lwp *lwp) d = lwp->fdt.fds[fd]; if (d) { - dfs_file_close(d); - fdt_fd_release(&lwp->fdt, fd); + dfs_fdtable_drop_fd(&lwp->fdt, fd); } fd--; } From 14008f8249eec25d320b568815edd60a42b68d73 Mon Sep 17 00:00:00 2001 From: bernard Date: Sun, 23 Aug 2026 07:44:06 +0800 Subject: [PATCH 2/9] [dfs][procfs] Add Linux-compatible process and system files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 补齐 procfs 中 cpuinfo、stat、uptime、loadavg、meminfo、mounts 和进程目录文件。 按架构和 SMP 配置隔离仅在对应平台有效的字段,避免非 AArch64/非 SMP 构建告警。 --- .../dfs_v2/filesystems/procfs/proc_cpuinfo.c | 76 ++- .../dfs_v2/filesystems/procfs/proc_loadavg.c | 56 ++- .../dfs_v2/filesystems/procfs/proc_meminfo.c | 23 +- .../dfs_v2/filesystems/procfs/proc_mounts.c | 75 ++- .../dfs/dfs_v2/filesystems/procfs/proc_pid.c | 450 +++++++++++++++--- .../dfs/dfs_v2/filesystems/procfs/proc_stat.c | 80 +++- .../dfs_v2/filesystems/procfs/proc_uptime.c | 23 +- 7 files changed, 660 insertions(+), 123 deletions(-) diff --git a/components/dfs/dfs_v2/filesystems/procfs/proc_cpuinfo.c b/components/dfs/dfs_v2/filesystems/procfs/proc_cpuinfo.c index 6f4983299280..ca3a8dc48acb 100644 --- a/components/dfs/dfs_v2/filesystems/procfs/proc_cpuinfo.c +++ b/components/dfs/dfs_v2/filesystems/procfs/proc_cpuinfo.c @@ -18,11 +18,19 @@ #include -static void *seq_start(struct dfs_seq_file *seq, off_t *index) +#if defined(__aarch64__) || defined(__AARCH64EL__) +static rt_uint64_t cpu_midr(void) { - off_t i = *index; // seq->index + rt_uint64_t value; - return NULL + (i == 0); + __asm__ volatile("mrs %0, midr_el1" : "=r"(value)); + return value; +} +#endif + +static void *seq_start(struct dfs_seq_file *seq, off_t *index) +{ + return (*index < RT_CPUS_NR) ? (void *)1 : RT_NULL; } static void seq_stop(struct dfs_seq_file *seq, void *data) @@ -31,27 +39,57 @@ static void seq_stop(struct dfs_seq_file *seq, void *data) static void *seq_next(struct dfs_seq_file *seq, void *data, off_t *index) { - /* data: The return value of the start or next*/ - off_t i = *index + 1; // seq->index - - *index = i; - - return NULL; + RT_UNUSED(seq); + RT_UNUSED(data); + *index += 1; + return (*index < RT_CPUS_NR) ? (void *)1 : RT_NULL; } static int seq_show(struct dfs_seq_file *seq, void *data) { - /* data: The return value of the start or next*/ - dfs_seq_puts(seq, "rt_weak const struct dfs_seq_ops *cpuinfo_get_seq_ops(void)\n--need your own function--\n"); +#if defined(__aarch64__) || defined(__AARCH64EL__) + rt_uint64_t midr; + rt_uint32_t implementer; + rt_uint32_t variant; + rt_uint32_t architecture; + rt_uint32_t part; + rt_uint32_t revision; +#endif + + RT_UNUSED(data); +#if defined(__aarch64__) || defined(__AARCH64EL__) + midr = cpu_midr(); + implementer = (rt_uint32_t)((midr >> 24) & 0xffU); + variant = (rt_uint32_t)((midr >> 20) & 0x0fU); + architecture = (rt_uint32_t)((midr >> 16) & 0x0fU); + part = (rt_uint32_t)((midr >> 4) & 0x0fffU); + revision = (rt_uint32_t)(midr & 0x0fU); +#endif + + dfs_seq_printf(seq, "processor\t: %lu\n", (unsigned long)seq->index); +#if defined(__aarch64__) || defined(__AARCH64EL__) + dfs_seq_puts(seq, "model name\t: ARMv8 Generic\n"); + dfs_seq_printf(seq, "CPU implementer\t: 0x%02lx\n", (unsigned long)implementer); + dfs_seq_printf(seq, "CPU architecture: %lu\n", (unsigned long)(architecture ? architecture : 8U)); + dfs_seq_printf(seq, "CPU variant\t: 0x%lx\n", (unsigned long)variant); + dfs_seq_printf(seq, "CPU part\t: 0x%03lx\n", (unsigned long)part); + dfs_seq_printf(seq, "CPU revision\t: %lu\n", (unsigned long)revision); +#elif defined(__riscv) + dfs_seq_puts(seq, "model name\t: RISC-V Generic\n"); + dfs_seq_puts(seq, "isa\t\t: rv64\n"); +#else + dfs_seq_puts(seq, "model name\t: RT-Thread Generic CPU\n"); +#endif + dfs_seq_puts(seq, "\n"); return 0; } static const struct dfs_seq_ops seq_ops = { - .start = seq_start, - .stop = seq_stop, - .next = seq_next, - .show = seq_show, + .start = seq_start, + .stop = seq_stop, + .next = seq_next, + .show = seq_show, }; rt_weak const struct dfs_seq_ops *cpuinfo_get_seq_ops(void) @@ -70,10 +108,10 @@ static int proc_close(struct dfs_file *file) } static const struct dfs_file_ops file_ops = { - .open = proc_open, - .read = dfs_seq_read, - .lseek = dfs_seq_lseek, - .close = proc_close, + .open = proc_open, + .read = dfs_seq_read, + .lseek = dfs_seq_lseek, + .close = proc_close, }; int proc_cpuinfo_init(void) diff --git a/components/dfs/dfs_v2/filesystems/procfs/proc_loadavg.c b/components/dfs/dfs_v2/filesystems/procfs/proc_loadavg.c index 6ce1bc487a71..acb6ee35b563 100644 --- a/components/dfs/dfs_v2/filesystems/procfs/proc_loadavg.c +++ b/components/dfs/dfs_v2/filesystems/procfs/proc_loadavg.c @@ -17,16 +17,62 @@ #include #include -#include +#ifdef RT_USING_SMART +#include "lwp_pid.h" +#ifdef RT_USING_MUSLLIBC +#include "lwp.h" +#endif +#endif -extern void rt_memory_info(rt_size_t *total, - rt_size_t *used, - rt_size_t *max_used); +#ifdef RT_USING_SMART +struct loadavg_count +{ + int total; + int runnable; +}; + +static int loadavg_count_pid(pid_t pid, void *arg) +{ + struct loadavg_count *count = (struct loadavg_count *)arg; + struct rt_lwp *lwp = lwp_from_pid_locked(pid); + + if (!lwp) + { + return 0; + } + count->total++; +#ifdef RT_USING_MUSLLIBC + { + rt_list_t *node; + + node = lwp->t_grp.next; + while (node != &lwp->t_grp) + { + rt_thread_t thread = rt_list_entry(node, struct rt_thread, sibling); + if (RT_SCHED_CTX(thread).stat == RT_THREAD_RUNNING) + { + count->runnable++; + break; + } + node = node->next; + } + } +#endif + return 0; +} +#endif static int single_show(struct dfs_seq_file *seq, void *data) { - dfs_seq_printf(seq, "0.13 0.16 0.17 1/1035 380436\n"); +#ifdef RT_USING_SMART + struct loadavg_count count = { 0, 0 }; + + lwp_pid_for_each(loadavg_count_pid, &count); + dfs_seq_printf(seq, "0.00 0.00 0.00 %d/%d 0\n", count.runnable, count.total); +#else + dfs_seq_puts(seq, "0.00 0.00 0.00 0/0 0\n"); +#endif return 0; } diff --git a/components/dfs/dfs_v2/filesystems/procfs/proc_meminfo.c b/components/dfs/dfs_v2/filesystems/procfs/proc_meminfo.c index b6ae56288f4a..b1617f8bfd77 100644 --- a/components/dfs/dfs_v2/filesystems/procfs/proc_meminfo.c +++ b/components/dfs/dfs_v2/filesystems/procfs/proc_meminfo.c @@ -34,24 +34,27 @@ static int single_show(struct dfs_seq_file *seq, void *data) total_sum = total_sum + total; total_freed = total_freed + total - used; - dfs_seq_printf(seq, "%-16s%8d KB\n", "MemMaxUsed:", max_used / 1024); - dfs_seq_printf(seq, "%-16s%8d KB\n", "MemAvailable:", (total - used) / 1024); - dfs_seq_printf(seq, "%-16s%8d KB\n", "Cached:", 0); - dfs_seq_printf(seq, "%-16s%8d KB\n", "SReclaimable:", 0); + dfs_seq_printf(seq, "%-16s%8lu kB\n", "MemMaxUsed:", (unsigned long)(max_used / 1024)); + dfs_seq_printf(seq, "%-16s%8lu kB\n", "MemAvailable:", (unsigned long)((total - used) / 1024)); rt_page_get_info(&total, &freed); total_sum = total_sum + total * RT_MM_PAGE_SIZE; total_freed = total_freed + freed * RT_MM_PAGE_SIZE; - dfs_seq_printf(seq, "%-16s%8d KB\n", "MemTotal:", total_sum / 1024); - dfs_seq_printf(seq, "%-16s%8d KB\n", "MemFree:", total_freed / 1024); - dfs_seq_printf(seq, "%-16s%8d KB\n", "LowPageTotal:", total * RT_MM_PAGE_SIZE / 1024); - dfs_seq_printf(seq, "%-16s%8d KB\n", "lowPageFree:", freed * RT_MM_PAGE_SIZE/ 1024); + dfs_seq_printf(seq, "%-16s%8lu kB\n", "MemTotal:", (unsigned long)(total_sum / 1024)); + dfs_seq_printf(seq, "%-16s%8lu kB\n", "MemFree:", (unsigned long)(total_freed / 1024)); + dfs_seq_printf(seq, "%-16s%8lu kB\n", "Buffers:", 0UL); + dfs_seq_printf(seq, "%-16s%8lu kB\n", "Cached:", 0UL); + dfs_seq_printf(seq, "%-16s%8lu kB\n", "SwapCached:", 0UL); + dfs_seq_printf(seq, "%-16s%8lu kB\n", "SwapTotal:", 0UL); + dfs_seq_printf(seq, "%-16s%8lu kB\n", "SwapFree:", 0UL); + dfs_seq_printf(seq, "%-16s%8lu kB\n", "LowPageTotal:", (unsigned long)(total * RT_MM_PAGE_SIZE / 1024)); + dfs_seq_printf(seq, "%-16s%8lu kB\n", "LowPageFree:", (unsigned long)(freed * RT_MM_PAGE_SIZE / 1024)); rt_page_high_get_info(&total, &freed); - dfs_seq_printf(seq, "%-16s%8d KB\n", "HighPageTotal:", total * RT_MM_PAGE_SIZE / 1024); - dfs_seq_printf(seq, "%-16s%8d KB\n", "HighPageFree:", freed * RT_MM_PAGE_SIZE / 1024); + dfs_seq_printf(seq, "%-16s%8lu kB\n", "HighPageTotal:", (unsigned long)(total * RT_MM_PAGE_SIZE / 1024)); + dfs_seq_printf(seq, "%-16s%8lu kB\n", "HighPageFree:", (unsigned long)(freed * RT_MM_PAGE_SIZE / 1024)); return 0; } diff --git a/components/dfs/dfs_v2/filesystems/procfs/proc_mounts.c b/components/dfs/dfs_v2/filesystems/procfs/proc_mounts.c index 49a6ba99c6b0..1bb3b6a6bc6e 100644 --- a/components/dfs/dfs_v2/filesystems/procfs/proc_mounts.c +++ b/components/dfs/dfs_v2/filesystems/procfs/proc_mounts.c @@ -19,13 +19,62 @@ #include #include +static void mnt_escape(const char *source, char *target, rt_size_t target_size) +{ + rt_size_t offset = 0; + const char *cursor = source ? source : ""; + + if (target_size == 0) + { + return; + } + + while (*cursor != '\0' && offset + 4 < target_size) + { + if (*cursor == ' ') + { + target[offset++] = '\\'; + target[offset++] = '0'; + target[offset++] = '4'; + target[offset++] = '0'; + } + else if (*cursor == '\t') + { + target[offset++] = '\\'; + target[offset++] = '0'; + target[offset++] = '1'; + target[offset++] = '1'; + } + else if (*cursor == '\n') + { + target[offset++] = '\\'; + target[offset++] = '0'; + target[offset++] = '1'; + target[offset++] = '2'; + } + else if (*cursor == '\\') + { + target[offset++] = '\\'; + target[offset++] = '1'; + target[offset++] = '3'; + target[offset++] = '4'; + } + else + { + target[offset++] = *cursor; + } + cursor++; + } + + target[offset] = '\0'; +} const char *mnt_flag(int flag) { - /*if (flag & MNT_READONLY) + if (flag & MNT_RDONLY) { return "ro"; - }*/ + } return "rw"; } @@ -33,19 +82,23 @@ const char *mnt_flag(int flag) static struct dfs_mnt* mnt_show(struct dfs_mnt *mnt, void *parameter) { struct dfs_seq_file *seq = (struct dfs_seq_file *)parameter; + char source[DFS_PATH_MAX]; + char target[DFS_PATH_MAX]; + const char *source_name; + const char *filesystem_name; - if (mnt) + if (mnt && mnt->fs_ops && mnt->fullpath) { - if (mnt->dev_id) - { - dfs_seq_printf(seq, "%s %s %s %s 0 0\n", mnt->dev_id->parent.name, mnt->fullpath, - mnt->fs_ops->name, mnt_flag(mnt->flags)); - } - else + filesystem_name = mnt->fs_ops->name ? mnt->fs_ops->name : "unknown"; + source_name = filesystem_name; + if (mnt->dev_id && mnt->dev_id->parent.name[0] != '\0') { - dfs_seq_printf(seq, "%s %s %s %s 0 0\n", mnt->fs_ops->name, mnt->fullpath, - mnt->fs_ops->name, mnt_flag(mnt->flags)); + source_name = mnt->dev_id->parent.name; } + mnt_escape(source_name, source, sizeof(source)); + mnt_escape(mnt->fullpath, target, sizeof(target)); + dfs_seq_printf(seq, "%s %s %s %s 0 0\n", source, target, + filesystem_name, mnt_flag(mnt->flags)); } return RT_NULL; diff --git a/components/dfs/dfs_v2/filesystems/procfs/proc_pid.c b/components/dfs/dfs_v2/filesystems/procfs/proc_pid.c index a413f646c830..9212351ac419 100644 --- a/components/dfs/dfs_v2/filesystems/procfs/proc_pid.c +++ b/components/dfs/dfs_v2/filesystems/procfs/proc_pid.c @@ -26,6 +26,12 @@ #include "lwp.h" #include "lwp_pid.h" #include +#ifdef ARCH_MM_MMU +#include +#include +#include +#include +#endif struct pid_dentry { @@ -49,6 +55,241 @@ static char stat_transform(int __stat) } } +#ifdef ARCH_MM_MMU +#define PROC_MAP_MAX 256 + +struct proc_map_entry +{ + uintptr_t start; + uintptr_t end; + uintptr_t offset; + rt_size_t resident_pages; + rt_bool_t writable; + rt_bool_t shared; + rt_bool_t executable; + char name[DFS_PATH_MAX]; +}; + +struct proc_map_context +{ + struct rt_lwp *lwp; + struct proc_map_entry *entries; + rt_size_t count; + rt_bool_t smaps; +}; + +static void proc_map_permissions(rt_varea_t varea, struct proc_map_entry *entry) +{ + entry->writable = rt_hw_mmu_attr_test_perm( + varea->attr, RT_HW_MMU_PROT_WRITE | RT_HW_MMU_PROT_USER); + entry->shared = (varea->flag & MMF_MAP_SHARED) != 0; + entry->executable = (varea->flag & MMF_TEXT) != 0; +} + +static void proc_map_name(rt_varea_t varea, struct proc_map_entry *entry) +{ + const char *name = RT_NULL; + + if (varea->mem_obj && varea->mem_obj->get_name) + { + name = varea->mem_obj->get_name(varea); + } + + if (!name || !name[0] || !rt_strcmp(name, "anonymous") || + !rt_strcmp(name, "reference") || !rt_strcmp(name, "null")) + { + name = "[anon]"; + } + rt_strncpy(entry->name, name, sizeof(entry->name) - 1); + entry->name[sizeof(entry->name) - 1] = '\0'; +} + +static int proc_map_collect(rt_varea_t varea, void *arg) +{ + struct proc_map_context *context = (struct proc_map_context *)arg; + struct proc_map_entry *entry; + uintptr_t address; + uintptr_t end; + + if (context->count >= PROC_MAP_MAX || !varea || varea->size == 0) + { + return 0; + } + + entry = &context->entries[context->count++]; + entry->start = (uintptr_t)varea->start; + entry->end = entry->start + varea->size; + entry->offset = (uintptr_t)varea->offset * ARCH_PAGE_SIZE; + entry->resident_pages = 0; + proc_map_permissions(varea, entry); + proc_map_name(varea, entry); + + address = entry->start; + end = entry->end; + while (address < end) + { + if (rt_hw_mmu_v2p(varea->aspace, (void *)address) != ARCH_MAP_FAILED) + { + entry->resident_pages++; + } + address += ARCH_PAGE_SIZE; + } + + return 0; +} + +static void proc_map_context_free(struct proc_map_context *context) +{ + if (context) + { + if (context->lwp) + { + lwp_from_pid_release_lock(context->lwp); + } + if (context->entries) + { + rt_free(context->entries); + } + rt_free(context); + } +} + +static void *proc_maps_start(struct dfs_seq_file *seq, off_t *index) +{ + struct proc_map_context *context = (struct proc_map_context *)seq->data; + + return context && *index < (off_t)context->count ? &context->entries[*index] : RT_NULL; +} + +static void *proc_maps_next(struct dfs_seq_file *seq, void *data, off_t *index) +{ + struct proc_map_context *context = (struct proc_map_context *)seq->data; + + RT_UNUSED(data); + *index += 1; + return context && *index < (off_t)context->count ? &context->entries[*index] : RT_NULL; +} + +static void proc_maps_stop(struct dfs_seq_file *seq, void *data) +{ + RT_UNUSED(seq); + RT_UNUSED(data); +} + +static int proc_maps_show(struct dfs_seq_file *seq, void *data) +{ + struct proc_map_entry *entry = (struct proc_map_entry *)data; + char permissions[5]; + + permissions[0] = 'r'; + permissions[1] = entry->writable ? 'w' : '-'; + permissions[2] = entry->executable ? 'x' : '-'; + permissions[3] = entry->shared ? 's' : 'p'; + permissions[4] = '\0'; + + dfs_seq_printf(seq, "%0*lx-%0*lx %s %0*lx 00:00 0", + (int)(sizeof(uintptr_t) * 2), (unsigned long)entry->start, + (int)(sizeof(uintptr_t) * 2), (unsigned long)entry->end, + permissions, (int)(sizeof(uintptr_t) * 2), + (unsigned long)entry->offset); + if (entry->name[0]) + { + dfs_seq_printf(seq, " %s", entry->name); + } + dfs_seq_puts(seq, "\n"); + + if (((struct proc_map_context *)seq->data)->smaps) + { + rt_size_t resident_kb = entry->resident_pages * (ARCH_PAGE_SIZE / 1024); + rt_size_t private_dirty = entry->writable && !entry->shared ? resident_kb : 0; + + dfs_seq_printf(seq, "Size: %lu kB\n", (unsigned long)((entry->end - entry->start) / 1024)); + dfs_seq_printf(seq, "Rss: %lu kB\n", (unsigned long)resident_kb); + dfs_seq_printf(seq, "Pss: %lu kB\n", (unsigned long)resident_kb); + dfs_seq_puts(seq, "Shared_Clean: 0 kB\n"); + dfs_seq_puts(seq, "Shared_Dirty: 0 kB\n"); + dfs_seq_puts(seq, "Private_Clean: 0 kB\n"); + dfs_seq_printf(seq, "Private_Dirty: %lu kB\n", (unsigned long)private_dirty); + dfs_seq_printf(seq, "Anonymous: %lu kB\n", entry->name[0] == '[' ? (unsigned long)resident_kb : 0UL); + dfs_seq_puts(seq, "Swap: 0 kB\n\n"); + } + + return 0; +} + +static const struct dfs_seq_ops proc_maps_seq_ops = { + .start = proc_maps_start, + .stop = proc_maps_stop, + .next = proc_maps_next, + .show = proc_maps_show, +}; + +static int proc_maps_open(struct dfs_file *file) +{ + struct proc_dentry *dentry = (struct proc_dentry *)file->vnode->data; + struct proc_map_context *context; + struct dfs_seq_file *seq; + rt_varea_t varea; + int ret; + + context = rt_calloc(1, sizeof(*context)); + if (!context) + { + return -RT_ENOMEM; + } + context->lwp = lwp_from_pid_and_lock(dentry->pid); + if (!context->lwp || !context->lwp->aspace) + { + proc_map_context_free(context); + return -RT_ENOENT; + } + context->smaps = !rt_strcmp(dentry->name, "smaps"); + context->entries = rt_calloc(PROC_MAP_MAX, sizeof(*context->entries)); + if (!context->entries) + { + proc_map_context_free(context); + return -RT_ENOMEM; + } + + RD_LOCK(context->lwp->aspace); + varea = ASPACE_VAREA_FIRST(context->lwp->aspace); + while (varea) + { + proc_map_collect(varea, context); + varea = ASPACE_VAREA_NEXT(varea); + } + RD_UNLOCK(context->lwp->aspace); + + ret = dfs_seq_open(file, &proc_maps_seq_ops); + if (ret != RT_EOK) + { + proc_map_context_free(context); + return ret; + } + seq = (struct dfs_seq_file *)file->data; + seq->data = context; + return RT_EOK; +} + +static int proc_maps_close(struct dfs_file *file) +{ + struct dfs_seq_file *seq = (struct dfs_seq_file *)file->data; + struct proc_map_context *context = seq ? (struct proc_map_context *)seq->data : RT_NULL; + int ret = dfs_seq_release(file); + + proc_map_context_free(context); + file->data = RT_NULL; + return ret; +} + +static const struct dfs_file_ops proc_maps_fops = { + .open = proc_maps_open, + .read = dfs_seq_read, + .lseek = dfs_seq_lseek, + .close = proc_maps_close, +}; +#endif /* ARCH_MM_MMU */ + static int stat_single_show(struct dfs_seq_file *seq, void *data) { struct proc_dentry *dentry = (struct proc_dentry *)seq->file->vnode->data; @@ -60,56 +301,53 @@ static int stat_single_show(struct dfs_seq_file *seq, void *data) int lwp_oncpu = RT_CPUS_NR; int lwp_oncpu_ok = 0; struct rt_lwp *lwp = RT_NULL; - char** argv = RT_NULL; + char **argv = RT_NULL; char *filename = RT_NULL; char *dot = RT_NULL; - lwp_pid_lock_take(); - - lwp = lwp_from_pid_locked(dentry->pid); - argv = lwp_get_command_line_args(lwp); + lwp = lwp_from_pid_and_lock(dentry->pid); + argv = lwp ? lwp_get_command_line_args(lwp) : RT_NULL; if (lwp) { - dfs_seq_printf(seq,"%d ",dentry->pid); + dfs_seq_printf(seq, "%d ", dentry->pid); if (argv) { - filename = strrchr(argv[0], '/'); - dot = strchr(argv[0], '.'); - - if (filename != NULL) + if (argv[0]) { - filename++; - } - else - { - filename = argv[0]; - } + filename = strrchr(argv[0], '/'); - if (dot != NULL) - { - *dot = '\0'; - } + if (filename != NULL) + { + filename++; + } + else + { + filename = argv[0]; + } - if (filename != NULL) - { - dfs_seq_printf(seq,"(%s) ", filename); + dot = strchr(filename, '.'); + if (dot != NULL) + { + *dot = '\0'; + } + + dfs_seq_printf(seq, "(%s) ", filename); } else { - dfs_seq_printf(seq,"(%s) ", argv[0]); + dfs_seq_printf(seq, "(%s) ", ""); } - lwp_free_command_line_args(argv); } else { - dfs_seq_printf(seq,"(%s) ", ""); + dfs_seq_printf(seq, "(%s) ", ""); } if (lwp->terminated) { - dfs_seq_printf(seq,"%c ",'Z'); + dfs_seq_printf(seq, "%c ", 'Z'); } else { @@ -120,11 +358,11 @@ static int stat_single_show(struct dfs_seq_file *seq, void *data) user_time_lwp = user_time_lwp + thread->user_time; system_time_lwp = system_time_lwp + thread->system_time; - #if RT_CPUS_NR > 1 - #define ONCPU(thread) RT_SCHED_CTX(thread).oncpu - #else - #define ONCPU(thread) 0 - #endif +#if RT_CPUS_NR > 1 +#define ONCPU(thread) RT_SCHED_CTX(thread).oncpu +#else +#define ONCPU(thread) 0 +#endif if (lwp_oncpu_ok == 0) { lwp_oncpu = ONCPU(thread); @@ -140,34 +378,37 @@ static int stat_single_show(struct dfs_seq_file *seq, void *data) if (mask == 1) { - dfs_seq_printf(seq,"%c ",'R'); + dfs_seq_printf(seq, "%c ", 'R'); } else { - dfs_seq_printf(seq,"%c ",'S'); + dfs_seq_printf(seq, "%c ", 'S'); } } - lwp_pid_lock_release(); - if (lwp->parent != NULL) - dfs_seq_printf(seq,"%d ",lwp->parent->pid); + { + dfs_seq_printf(seq, "%d ", lwp->parent->pid); + } else - dfs_seq_printf(seq,"0 "); + { + dfs_seq_printf(seq, "0 "); + } dfs_seq_printf(seq, "1 1 0 -1 4194560 48245 133976064 732 425574 "); - dfs_seq_printf(seq,"%llu ",user_time_lwp);//utime - dfs_seq_printf(seq,"%llu ",system_time_lwp);//stime + dfs_seq_printf(seq, "%llu ", user_time_lwp);//utime + dfs_seq_printf(seq, "%llu ", system_time_lwp);//stime dfs_seq_printf(seq, "1204291 518742 20 0 1 0 50 "); - dfs_seq_printf(seq, "%d ",rt_aspace_count_vsz(lwp->aspace));//VSZ +#ifdef ARCH_MM_MMU + dfs_seq_printf(seq, "%lu ", lwp->aspace ? (unsigned long)rt_aspace_count_vsz(lwp->aspace) : 0UL);//VSZ +#else + dfs_seq_puts(seq, "0 "); +#endif dfs_seq_printf(seq, "1422 18446744073709551615 "); dfs_seq_printf(seq, "1 1 0 0 0 0 671173123 4096 1260 0 0 0 17 "); dfs_seq_printf(seq, "%d ", lwp_oncpu);//CPU dfs_seq_printf(seq, "0 0 0 0 0 0 0 0 0 0 0 0 0"); - dfs_seq_printf(seq,"\n"); - } - else - { - lwp_pid_lock_release(); + dfs_seq_printf(seq, "\n"); + lwp_from_pid_release_lock(lwp); } return 0; @@ -177,12 +418,10 @@ static int cmdline_single_show(struct dfs_seq_file *seq, void *data) { struct proc_dentry *dentry = (struct proc_dentry *)seq->file->vnode->data; struct rt_lwp *lwp; - char** argv; + char **argv; - lwp_pid_lock_take(); - lwp = lwp_from_pid_locked(dentry->pid); - argv = lwp_get_command_line_args(lwp); - lwp_pid_lock_release(); + lwp = lwp_from_pid_and_lock(dentry->pid); + argv = lwp ? lwp_get_command_line_args(lwp) : RT_NULL; if (argv) { @@ -198,10 +437,83 @@ static int cmdline_single_show(struct dfs_seq_file *seq, void *data) { dfs_seq_puts(seq, "error\n"); } + lwp_from_pid_release_lock(lwp); return 0; } +static int proc_pid_status_show(struct dfs_seq_file *seq, void *data) +{ + struct proc_dentry *dentry = (struct proc_dentry *)seq->file->vnode->data; + struct rt_lwp *lwp = lwp_from_pid_and_lock(dentry->pid); + rt_size_t vm_size = 0; + rt_size_t thread_count = 0; + rt_list_t *node; + char state; + + RT_UNUSED(data); + if (!lwp) + { + return -RT_ENOENT; + } + +#ifdef ARCH_MM_MMU + if (lwp->aspace) + { + vm_size = rt_aspace_count_vsz(lwp->aspace); + } +#endif + node = lwp->t_grp.next; + while (node != &lwp->t_grp) + { + thread_count++; + node = node->next; + } + state = lwp->terminated ? 'Z' : 'S'; + dfs_seq_printf(seq, "Name:\t%s\n", lwp->cmd); + dfs_seq_printf(seq, "State:\t%c (sleeping)\n", state); + dfs_seq_printf(seq, "Pid:\t%d\n", lwp->pid); + dfs_seq_printf(seq, "PPid:\t%d\n", lwp->parent ? lwp->parent->pid : 0); + dfs_seq_puts(seq, "Uid:\t0\t0\t0\t0\nGid:\t0\t0\t0\t0\n"); + dfs_seq_printf(seq, "Threads:\t%lu\n", (unsigned long)thread_count); + dfs_seq_printf(seq, "VmSize:\t%lu kB\n", (unsigned long)(vm_size / 1024)); + dfs_seq_printf(seq, "VmRSS:\t%lu kB\n", (unsigned long)(vm_size / 1024)); + dfs_seq_puts(seq, "VmData:\t0 kB\nVmStk:\t0 kB\nVmExe:\t0 kB\nVmLib:\t0 kB\n"); + lwp_from_pid_release_lock(lwp); + return 0; +} + +static int proc_pid_statm_show(struct dfs_seq_file *seq, void *data) +{ + struct proc_dentry *dentry = (struct proc_dentry *)seq->file->vnode->data; + struct rt_lwp *lwp = lwp_from_pid_and_lock(dentry->pid); + rt_size_t size_pages = 0; + rt_size_t text_pages = 0; + rt_size_t data_pages = 0; + + RT_UNUSED(data); + if (!lwp) + { + return -RT_ENOENT; + } + +#ifdef ARCH_MM_MMU + if (lwp->aspace) + { + size_pages = (rt_aspace_count_vsz(lwp->aspace) + ARCH_PAGE_SIZE - 1) / ARCH_PAGE_SIZE; + } +#endif +#ifdef ARCH_MM_MMU + text_pages = (lwp->text_size + ARCH_PAGE_SIZE - 1) / ARCH_PAGE_SIZE; + data_pages = (lwp->data_size + ARCH_PAGE_SIZE - 1) / ARCH_PAGE_SIZE; +#endif + dfs_seq_printf(seq, "%lu %lu 0 %lu 0 %lu 0\n", + (unsigned long)size_pages, (unsigned long)size_pages, + (unsigned long)text_pages, (unsigned long)data_pages); + lwp_from_pid_release_lock(lwp); + return 0; +} + struct proc_dentry *proc_pid_fd_lookup(struct proc_dentry *parent, const char *name) { struct proc_dentry *dentry = RT_NULL; @@ -240,13 +552,21 @@ struct proc_dentry *proc_pid_fd_lookup(struct proc_dentry *parent, const char *n { //todo add vnode->data if (file->vnode->type == FT_SOCKET) + { dentry->data = (void *)rt_strdup("socket"); + } else if (file->vnode->type == FT_USER) + { dentry->data = (void *)rt_strdup("user"); + } else if (file->vnode->type == FT_DEVICE) + { dentry->data = (void *)rt_strdup("device"); + } else + { dentry->data = (void *)rt_strdup("unknown"); + } } dentry->pid = parent->pid; @@ -337,8 +657,9 @@ int proc_pid_exe_readlink(struct proc_dentry *dentry, char *buf, int len) { struct rt_lwp *lwp; - lwp = lwp_self(); - len = rt_snprintf(buf, len, "%s", lwp ? lwp->exe_file : "null"); + lwp = lwp_from_pid_and_lock(dentry->pid); + len = rt_snprintf(buf, len, "%s", lwp && lwp->exe_file ? lwp->exe_file : "null"); + lwp_from_pid_release_lock(lwp); return len; } @@ -351,8 +672,9 @@ int proc_pid_cwd_readlink(struct proc_dentry *dentry, char *buf, int len) { struct rt_lwp *lwp; - lwp = lwp_self(); + lwp = lwp_from_pid_and_lock(dentry->pid); len = rt_snprintf(buf, len, "%s", lwp ? lwp->working_directory : "null"); + lwp_from_pid_release_lock(lwp); return len; } @@ -362,17 +684,23 @@ static const struct proc_ops proc_pid_cwd_ops = { }; static struct pid_dentry pid_dentry_base[] = { - {"cmdline", S_IFREG | S_IRUSR | S_IRGRP | S_IROTH, 0, 0, 0, cmdline_single_show, 0}, - {"cwd", S_IFLNK | S_IRUSR | S_IXUSR, 0, &proc_pid_cwd_ops, 0, 0}, - {"exe", S_IFLNK | S_IRUSR | S_IXUSR, 0, &proc_pid_exe_ops, 0, 0}, - {"fd", S_IFDIR | S_IRUSR | S_IXUSR, &proc_pid_fd_fops, &proc_pid_fd_ops, 0, 0, 0}, - {"mounts", S_IFLNK | S_IRUSR | S_IXUSR, 0, 0, 0, 0, "/proc/mounts"}, - {"stat", S_IFREG | S_IRUSR | S_IRGRP | S_IROTH, 0, 0, 0, stat_single_show, 0}, + { "cmdline", S_IFREG | S_IRUSR | S_IRGRP | S_IROTH, 0, 0, 0, cmdline_single_show, 0 }, + { "status", S_IFREG | S_IRUSR | S_IRGRP | S_IROTH, 0, 0, 0, proc_pid_status_show, 0 }, + { "statm", S_IFREG | S_IRUSR | S_IRGRP | S_IROTH, 0, 0, 0, proc_pid_statm_show, 0 }, + { "cwd", S_IFLNK | S_IRUSR | S_IXUSR, 0, &proc_pid_cwd_ops, 0, 0 }, + { "exe", S_IFLNK | S_IRUSR | S_IXUSR, 0, &proc_pid_exe_ops, 0, 0 }, + { "fd", S_IFDIR | S_IRUSR | S_IXUSR, &proc_pid_fd_fops, &proc_pid_fd_ops, 0, 0, 0 }, + { "mounts", S_IFLNK | S_IRUSR | S_IXUSR, 0, 0, 0, 0, "/proc/mounts" }, + { "stat", S_IFREG | S_IRUSR | S_IRGRP | S_IROTH, 0, 0, 0, stat_single_show, 0 }, +#ifdef ARCH_MM_MMU + { "maps", S_IFREG | S_IRUSR | S_IRGRP | S_IROTH, &proc_maps_fops, 0, 0, 0, 0 }, + { "smaps", S_IFREG | S_IRUSR | S_IRGRP | S_IROTH, &proc_maps_fops, 0, 0, 0, 0 }, +#endif }; int proc_pid(int pid) { - char pid_str[64] = {0}; + char pid_str[64] = { 0 }; struct proc_dentry *dentry; rt_snprintf(pid_str, 64, "%d", pid); diff --git a/components/dfs/dfs_v2/filesystems/procfs/proc_stat.c b/components/dfs/dfs_v2/filesystems/procfs/proc_stat.c index aeba99de5134..c24adb291e52 100644 --- a/components/dfs/dfs_v2/filesystems/procfs/proc_stat.c +++ b/components/dfs/dfs_v2/filesystems/procfs/proc_stat.c @@ -18,6 +18,49 @@ #include +#ifdef RT_USING_SMART +#include "lwp_pid.h" +#ifdef RT_USING_MUSLLIBC +#include "lwp.h" +#endif +#endif + +#ifdef RT_USING_SMART +struct stat_process_count +{ + int total; + int running; +}; + +static int stat_process_count(pid_t pid, void *arg) +{ + struct stat_process_count *count = (struct stat_process_count *)arg; + struct rt_lwp *lwp = lwp_from_pid_locked(pid); + + if (!lwp) + { + return 0; + } + count->total++; +#ifdef RT_USING_MUSLLIBC + { + rt_list_t *node = lwp->t_grp.next; + + while (node != &lwp->t_grp) + { + rt_thread_t thread = rt_list_entry(node, struct rt_thread, sibling); + if (RT_SCHED_CTX(thread).stat == RT_THREAD_RUNNING) + { + count->running++; + break; + } + node = node->next; + } + } +#endif + return 0; +} +#endif static void *seq_start(struct dfs_seq_file *seq, off_t *index) { @@ -50,7 +93,7 @@ static int seq_show(struct dfs_seq_file *seq, void *data) for (i = 0; i < RT_CPUS_NR; i++) { - pcpu = rt_cpu_index(i); + pcpu = rt_cpu_index(i); user_total = user_total + pcpu->cpu_stat.user; system_total = system_total + pcpu->cpu_stat.system; idle_total = idle_total + pcpu->cpu_stat.idle; @@ -59,27 +102,36 @@ static int seq_show(struct dfs_seq_file *seq, void *data) for (i = 0; i < RT_CPUS_NR; i++) { - pcpu = rt_cpu_index(i); - dfs_seq_printf(seq, "cpu%d ",i); - dfs_seq_printf(seq, "%llu ",pcpu->cpu_stat.user);//user + pcpu = rt_cpu_index(i); + dfs_seq_printf(seq, "cpu%d ", i); + dfs_seq_printf(seq, "%llu ", pcpu->cpu_stat.user);//user dfs_seq_printf(seq, "0 ");//nice - dfs_seq_printf(seq, "%llu ",pcpu->cpu_stat.system);//system - dfs_seq_printf(seq, "%llu ",pcpu->cpu_stat.idle);//idle + dfs_seq_printf(seq, "%llu ", pcpu->cpu_stat.system);//system + dfs_seq_printf(seq, "%llu ", pcpu->cpu_stat.idle);//idle dfs_seq_printf(seq, "0 ");//iowait dfs_seq_printf(seq, "0 ");//irq dfs_seq_printf(seq, "0 ");//softirq dfs_seq_printf(seq, "0 0 0\n");//steal,guest,guest_nice + } +#ifdef RT_USING_SMART + { + struct stat_process_count process_count = { 0, 0 }; + lwp_pid_for_each(stat_process_count, &process_count); + dfs_seq_printf(seq, "processes %d\n", process_count.total); + dfs_seq_printf(seq, "procs_running %d\n", process_count.running); + dfs_seq_puts(seq, "procs_blocked 0\n"); } +#endif return 0; } static const struct dfs_seq_ops seq_ops = { - .start = seq_start, - .stop = seq_stop, - .next = seq_next, - .show = seq_show, + .start = seq_start, + .stop = seq_stop, + .next = seq_next, + .show = seq_show, }; rt_weak const struct dfs_seq_ops *stat_get_seq_ops(void) @@ -98,10 +150,10 @@ static int proc_close(struct dfs_file *file) } static const struct dfs_file_ops file_ops = { - .open = proc_open, - .read = dfs_seq_read, - .lseek = dfs_seq_lseek, - .close = proc_close, + .open = proc_open, + .read = dfs_seq_read, + .lseek = dfs_seq_lseek, + .close = proc_close, }; int proc_stat_init(void) diff --git a/components/dfs/dfs_v2/filesystems/procfs/proc_uptime.c b/components/dfs/dfs_v2/filesystems/procfs/proc_uptime.c index f16ab72b6672..5da9e9952d36 100644 --- a/components/dfs/dfs_v2/filesystems/procfs/proc_uptime.c +++ b/components/dfs/dfs_v2/filesystems/procfs/proc_uptime.c @@ -21,9 +21,26 @@ static int single_show(struct dfs_seq_file *seq, void *data) { - dfs_seq_printf(seq, "%lu.%02lu %lu.%02lu\n", - (unsigned long)rt_tick_get_millisecond() / 1000, (unsigned long)(rt_tick_get_millisecond() % 1000) / 100, - (unsigned long)rt_tick_get_millisecond() / 1000, (unsigned long)(rt_tick_get_millisecond() % 1000) / 100); + rt_tick_t ticks = rt_tick_get(); + rt_uint64_t uptime_ms; + rt_uint64_t idle_ticks = 0; +#ifdef RT_USING_SMP + int index; +#endif + + uptime_ms = ((rt_uint64_t)ticks * 1000U) / RT_TICK_PER_SECOND; +#ifdef RT_USING_SMP + for (index = 0; index < RT_CPUS_NR; index++) + { + idle_ticks += rt_cpu_index(index)->cpu_stat.idle; + } +#else + idle_ticks = rt_cpu_index(0)->cpu_stat.idle; +#endif + dfs_seq_printf(seq, "%llu.%02llu %llu.%02llu\n", + uptime_ms / 1000U, (uptime_ms % 1000U) / 10U, + ((rt_uint64_t)idle_ticks * 1000U) / RT_TICK_PER_SECOND / 1000U, + (((rt_uint64_t)idle_ticks * 1000U) / RT_TICK_PER_SECOND % 1000U) / 10U); return 0; } From 1fa803d721bab7e55fb0fedfb80a42be913a042a Mon Sep 17 00:00:00 2001 From: bernard Date: Sun, 23 Aug 2026 07:44:06 +0800 Subject: [PATCH 3/9] [libc][posix] Fix epoll waiters and LWP syscall copies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复 epoll waiter 生命周期,避免等待队列节点在回调后失效。 整理 LWP POSIX syscall 的用户空间拷贝和错误返回路径。 --- components/libc/posix/io/epoll/epoll.c | 102 +++++++-- components/lwp/lwp_syscall.c | 273 ++++++++++++++++--------- 2 files changed, 259 insertions(+), 116 deletions(-) diff --git a/components/libc/posix/io/epoll/epoll.c b/components/libc/posix/io/epoll/epoll.c index a4c9dcfc0628..9aa11e2d87fb 100644 --- a/components/libc/posix/io/epoll/epoll.c +++ b/components/libc/posix/io/epoll/epoll.c @@ -31,6 +31,15 @@ EPOLLET | EPOLLEXCLUSIVE) struct rt_eventpoll; +struct rt_fd_list; + +struct rt_epoll_waiter +{ + struct rt_wqueue_node wqn; + struct rt_fd_list *fdlist; + struct rt_epoll_waiter *next; + rt_bool_t queued; +}; enum rt_epoll_status { RT_EPOLL_STAT_INIT, @@ -45,7 +54,7 @@ struct rt_fd_list struct epoll_event epev; /**< Epoll event structure */ rt_pollreq_t req; /**< Poll request structure */ struct rt_eventpoll *ep; /**< Pointer to the associated event poll */ - struct rt_wqueue_node wqn; /**< Wait queue node */ + struct rt_epoll_waiter *waiters; /**< Wait nodes registered by poll */ int exclusive; /**< Indicates if the event is exclusive */ rt_bool_t is_rdl_node; /**< Indicates if the node is in the ready list */ int fd; /**< File descriptor */ @@ -71,6 +80,42 @@ static int epoll_poll(struct dfs_file *file, struct rt_pollreq *req); static int epoll_get_event(struct rt_fd_list *fl, rt_pollreq_t *req); static int epoll_do_ctl(int epfd, int op, int fd, struct epoll_event *event); +static void epoll_remove_waiters(struct rt_fd_list *fdlist) +{ + struct rt_epoll_waiter *waiter; + + while (fdlist->waiters != RT_NULL) + { + waiter = fdlist->waiters; + fdlist->waiters = waiter->next; + if (waiter->queued) + { + rt_wqueue_remove(&waiter->wqn); + waiter->queued = RT_FALSE; + } + rt_free(waiter); + } +} + +static void epoll_set_polling_thread(struct rt_eventpoll *ep, + rt_thread_t thread) +{ + struct rt_epoll_waiter *waiter; + struct rt_fd_list *fdlist; + + rt_mutex_take(&ep->lock, RT_WAITING_FOREVER); + ep->polling_thread = thread; + for (fdlist = ep->fdlist; fdlist != RT_NULL; fdlist = fdlist->next) + { + for (waiter = fdlist->waiters; waiter != RT_NULL; + waiter = waiter->next) + { + waiter->wqn.polling_thread = thread; + } + } + rt_mutex_release(&ep->lock); +} + static const struct dfs_file_ops epoll_fops = { .close = epoll_close, @@ -96,7 +141,7 @@ static int epoll_close_fdlist(struct rt_fd_list *fdlist) while (list->next != RT_NULL) { fre_node = list->next; - rt_wqueue_remove(&fre_node->wqn); + epoll_remove_waiters(fre_node); list->next = fre_node->next; rt_free(fre_node); @@ -196,6 +241,7 @@ static int epoll_poll(struct dfs_file *file, struct rt_pollreq *req) static int epoll_wqueue_callback(struct rt_wqueue_node *wait, void *key) { struct rt_fd_list *fdlist; + struct rt_epoll_waiter *waiter; struct rt_eventpoll *ep; rt_base_t level; int is_waiting = 0; @@ -203,7 +249,8 @@ static int epoll_wqueue_callback(struct rt_wqueue_node *wait, void *key) if (key && !((rt_ubase_t)key & wait->key)) return -1; - fdlist = rt_container_of(wait, struct rt_fd_list, wqn); + waiter = rt_container_of(wait, struct rt_epoll_waiter, wqn); + fdlist = waiter->fdlist; ep = fdlist->ep; if (ep) @@ -242,17 +289,34 @@ static void epoll_wqueue_add_callback(rt_wqueue_t *wq, rt_pollreq_t *req) { struct rt_fd_list *fdlist; struct rt_eventpoll *ep; + struct rt_epoll_waiter *waiter; fdlist = rt_container_of(req, struct rt_fd_list, req); ep = fdlist->ep; - fdlist->wqn.key = req->_key; - - rt_list_init(&(fdlist->wqn.list)); + for (waiter = fdlist->waiters; waiter != RT_NULL; waiter = waiter->next) + { + if (waiter->wqn.wqueue == wq) + { + waiter->wqn.key |= req->_key; + return; + } + } - fdlist->wqn.polling_thread = ep->polling_thread; - fdlist->wqn.wakeup = epoll_wqueue_callback; - rt_wqueue_add(wq, &fdlist->wqn); + waiter = (struct rt_epoll_waiter *)rt_calloc(1, sizeof(*waiter)); + if (waiter == RT_NULL) + { + return; + } + waiter->fdlist = fdlist; + waiter->wqn.key = req->_key; + waiter->wqn.polling_thread = ep->polling_thread; + waiter->wqn.wakeup = epoll_wqueue_callback; + rt_list_init(&waiter->wqn.list); + waiter->next = fdlist->waiters; + fdlist->waiters = waiter; + rt_wqueue_add(wq, &waiter->wqn); + waiter->queued = RT_TRUE; } /** @@ -346,6 +410,7 @@ static int epoll_epf_init(int fd) ep->fdlist->next = RT_NULL; ep->fdlist->fd = fd; ep->fdlist->ep = ep; + ep->fdlist->waiters = RT_NULL; ep->fdlist->exclusive = 0; ep->fdlist->is_rdl_node = RT_FALSE; dfs_vnode_init(df->vnode, FT_REGULAR, &epoll_fops); @@ -457,6 +522,7 @@ static int epoll_ctl_add(struct dfs_file *df, int fd, struct epoll_event *event) memcpy(&fdlist->epev.data, &event->data, sizeof(event->data)); fdlist->epev.events = 0; fdlist->ep = ep; + fdlist->waiters = RT_NULL; fdlist->exclusive = 0; fdlist->is_rdl_node = RT_FALSE; fdlist->req._proc = epoll_wqueue_add_callback; @@ -520,8 +586,7 @@ static int epoll_ctl_del(struct dfs_file *df, int fd) fre_fd = fdlist->next; fdlist->next = fdlist->next->next; - if (fre_fd->wqn.wqueue) - rt_wqueue_remove(&fre_fd->wqn); + epoll_remove_waiters(fre_fd); rt_free(fre_fd); break; @@ -570,8 +635,7 @@ static int epoll_ctl_mod(struct dfs_file *df, int fd, struct epoll_event *event) rt_mutex_take(&ep->lock, RT_WAITING_FOREVER); memcpy(&fdlist->next->epev.data, &event->data, sizeof(event->data)); fdlist->next->revents = event->events; - if (fdlist->next->wqn.wqueue) - rt_wqueue_remove(&fdlist->next->wqn); + epoll_remove_waiters(fdlist->next); rt_mutex_release(&ep->lock); epoll_ctl_install(fdlist->next, ep); @@ -804,10 +868,7 @@ static int epoll_do(struct rt_eventpoll *ep, struct epoll_event *events, int max isn_add = 0; if (event_num < maxevents) { - if (rdlist->wqn.wqueue) - { - rt_wqueue_remove(&rdlist->wqn); - } + epoll_remove_waiters(rdlist); mask = epoll_get_event(rdlist, &rdlist->req); @@ -825,8 +886,7 @@ static int epoll_do(struct rt_eventpoll *ep, struct epoll_event *events, int max { rdlist->revents = 0; isfree = 1; - if (rdlist->wqn.wqueue) - rt_wqueue_remove(&rdlist->wqn); + epoll_remove_waiters(rdlist); } else { @@ -864,7 +924,7 @@ static int epoll_do(struct rt_eventpoll *ep, struct epoll_event *events, int max else { level = rt_spin_lock_irqsave(&ep->spinlock); - if (!rdlist->wqn.wqueue) + if (rdlist->waiters == RT_NULL) { epoll_get_event(rdlist, &rdlist->req); } @@ -937,6 +997,7 @@ static int epoll_do_wait(int epfd, struct epoll_event *events, int maxevents, in ep = (struct rt_eventpoll *)df->vnode->data; if (ep) { + epoll_set_polling_thread(ep, rt_thread_self()); ret = epoll_do(ep, events, maxevents, timeout); } } @@ -1039,4 +1100,3 @@ int epoll_pwait2(int epfd, struct epoll_event *events, int maxevents, int timeou { return epoll_do_wait(epfd, events, maxevents, timeout, ss); } - diff --git a/components/lwp/lwp_syscall.c b/components/lwp/lwp_syscall.c index ea096f2a906c..a871f71b513e 100644 --- a/components/lwp/lwp_syscall.c +++ b/components/lwp/lwp_syscall.c @@ -425,20 +425,18 @@ ssize_t sys_read(int fd, void *buf, size_t nbyte) void *kmem = RT_NULL; ssize_t ret = -1; - if (!nbyte) - { - return -EINVAL; - } - - if (!lwp_user_accessable((void *)buf, nbyte)) + if (nbyte) { - return -EFAULT; - } + if (!lwp_user_accessable((void *)buf, nbyte)) + { + return -EFAULT; + } - kmem = kmem_get(nbyte); - if (!kmem) - { - return -ENOMEM; + kmem = kmem_get(nbyte); + if (!kmem) + { + return -ENOMEM; + } } ret = read(fd, kmem, nbyte); @@ -794,6 +792,28 @@ sysret_t sys_close(int fd) */ sysret_t sys_ioctl(int fd, unsigned long cmd, void *data) { +#ifdef ARCH_MM_MMU + if (cmd == F_GETLK || cmd == F_SETLK || cmd == F_SETLKW) + { + struct flock lock; + int ret; + + if (data == RT_NULL || + !lwp_user_accessable(data, sizeof(lock)) || + lwp_get_from_user(&lock, data, sizeof(lock)) != sizeof(lock)) + { + return -EFAULT; + } + + ret = ioctl(fd, cmd, &lock); + if (ret >= 0 && cmd == F_GETLK && + lwp_put_to_user(data, &lock, sizeof(lock)) != sizeof(lock)) + { + return -EFAULT; + } + return ret < 0 ? GET_ERRNO() : ret; + } +#endif int ret = ioctl(fd, cmd, data); return (ret < 0 ? GET_ERRNO() : ret); } @@ -5680,7 +5700,9 @@ static void cmsg_level_lwip_2_muslc(struct msghdr *message) static int copy_msghdr_from_user(struct msghdr *kmsg, struct musl_msghdr *umsg, struct iovec **out_iov, void **out_msg_control, - void **out_msg_name, void **out_buffer) + void **out_msg_name, void **out_buffer, + socklen_t *out_user_name_length, + rt_bool_t receive) { int index; size_t iovs_size; @@ -5704,12 +5726,33 @@ static int copy_msghdr_from_user(struct msghdr *kmsg, kmsg->msg_controllen = user_message.msg_controllen; kmsg->msg_flags = user_message.msg_flags; + if (out_user_name_length != RT_NULL) + { + *out_user_name_length = kmsg->msg_namelen; + } + if (kmsg->msg_iovlen < 0 || (size_t)kmsg->msg_iovlen > SIZE_MAX / sizeof(*kmsg->msg_iov) || - kmsg->msg_namelen > sizeof(union lwp_sockaddr_buffer)) + (!receive && + kmsg->msg_namelen > sizeof(union lwp_sockaddr_buffer))) { return -EINVAL; } + if (receive) + { + if (kmsg->msg_name == RT_NULL) + { + kmsg->msg_namelen = 0; + } + else if (kmsg->msg_namelen > sizeof(union lwp_sockaddr_buffer)) + { + kmsg->msg_namelen = sizeof(union lwp_sockaddr_buffer); + } + if (kmsg->msg_control == RT_NULL) + { + kmsg->msg_controllen = 0; + } + } if (kmsg->msg_name != RT_NULL && !lwp_user_accessable(kmsg->msg_name, kmsg->msg_namelen)) { @@ -5862,6 +5905,7 @@ static int copy_msghdr_from_user(struct msghdr *kmsg, sysret_t sys_recvmsg(int socket, struct musl_msghdr *msg, int flags) { int flgs, ret = -1; + int error = 0; struct msghdr kmsg; #ifdef ARCH_MM_MMU int index; @@ -5882,62 +5926,67 @@ sysret_t sys_recvmsg(int socket, struct musl_msghdr *msg, int flags) #ifdef ARCH_MM_MMU ret = copy_msghdr_from_user(&kmsg, msg, &uiov, &msg_control, - &msg_name, &buffer); + &msg_name, &buffer, &user_name_length, + RT_TRUE); - if (!ret) + if (ret < 0) { - user_name_length = kmsg.msg_namelen; - ret = recvmsg(socket, &kmsg, flgs); - - if (ret < 0) - { - goto _free_res; - } - - kiov = kmsg.msg_iov; - remaining = (size_t)ret; + return ret; + } + ret = recvmsg(socket, &kmsg, flgs); + if (ret < 0) + { + error = GET_ERRNO(); + } - for (index = 0; index < kmsg.msg_iovlen && remaining != 0; ++index) - { - size_t copy_length = kiov->iov_len; + if (ret < 0) + { + goto _free_res; + } - if (copy_length > remaining) - { - copy_length = remaining; - } - lwp_put_to_user(uiov->iov_base, kiov->iov_base, copy_length); - remaining -= copy_length; + kiov = kmsg.msg_iov; + remaining = (size_t)ret; - ++kiov; - ++uiov; - } + for (index = 0; index < kmsg.msg_iovlen && remaining != 0; ++index) + { + size_t copy_length = kiov->iov_len; - if (msg_control != RT_NULL && kmsg.msg_controllen != 0) - { - cmsg_level_lwip_2_muslc(&kmsg); - lwp_put_to_user(msg_control, kmsg.msg_control, - kmsg.msg_controllen); - } - if (msg_name != RT_NULL && kmsg.msg_name != RT_NULL) + if (copy_length > remaining) { - socklen_t name_length; - - name_length = lwp_sockaddr_to_user( - (struct musl_sockaddr *)msg_name, user_name_length, - (const union lwp_sockaddr_buffer *)kmsg.msg_name, - kmsg.msg_namelen); - lwp_put_to_user(&msg->msg_namelen, &name_length, - sizeof(name_length)); + copy_length = remaining; } - kmsg.msg_flags = netflags_lwip_2_muslc(kmsg.msg_flags); - lwp_put_to_user(&msg->msg_flags, &kmsg.msg_flags, sizeof(kmsg.msg_flags)); - lwp_put_to_user(&msg->msg_controllen, &kmsg.msg_controllen, - sizeof(kmsg.msg_controllen)); + lwp_put_to_user(uiov->iov_base, kiov->iov_base, copy_length); + remaining -= copy_length; + + ++kiov; + ++uiov; + } + + if (msg_control != RT_NULL && kmsg.msg_controllen != 0) + { + cmsg_level_lwip_2_muslc(&kmsg); + lwp_put_to_user(msg_control, kmsg.msg_control, + kmsg.msg_controllen); + } + if (msg_name != RT_NULL && kmsg.msg_name != RT_NULL) + { + socklen_t name_length; - _free_res: - kmem_put(buffer); - kmem_put(kmsg.msg_iov); + name_length = lwp_sockaddr_to_user( + (struct musl_sockaddr *)msg_name, user_name_length, + (const union lwp_sockaddr_buffer *)kmsg.msg_name, + kmsg.msg_namelen); + lwp_put_to_user(&msg->msg_namelen, &name_length, + sizeof(name_length)); } + kmsg.msg_flags = netflags_lwip_2_muslc(kmsg.msg_flags); + lwp_put_to_user(&msg->msg_flags, &kmsg.msg_flags, sizeof(kmsg.msg_flags)); + lwp_put_to_user(&msg->msg_controllen, &kmsg.msg_controllen, + sizeof(kmsg.msg_controllen)); + +_free_res: + kmem_put(buffer); + kmem_put(kmsg.msg_iov); #else kmsg.msg_name = msg->msg_name; kmsg.msg_namelen = msg->msg_namelen; @@ -5948,6 +5997,10 @@ sysret_t sys_recvmsg(int socket, struct musl_msghdr *msg, int flags) kmsg.msg_flags = msg->msg_flags; ret = recvmsg(socket, &kmsg, flgs); + if (ret < 0) + { + error = GET_ERRNO(); + } if (!ret) { @@ -5955,7 +6008,11 @@ sysret_t sys_recvmsg(int socket, struct musl_msghdr *msg, int flags) } #endif /* ARCH_MM_MMU */ - return (ret < 0 ? GET_ERRNO() : ret); + if (ret >= 0) + { + return ret; + } + return error != 0 ? error : ret; } /** @@ -6178,6 +6235,7 @@ sysret_t sys_recv(int socket, void *mem, size_t len, int flags) sysret_t sys_sendmsg(int socket, const struct musl_msghdr *msg, int flags) { int flgs, ret = -1; + int error = 0; struct msghdr kmsg; #ifdef ARCH_MM_MMU int index; @@ -6195,47 +6253,53 @@ sysret_t sys_sendmsg(int socket, const struct musl_msghdr *msg, int flags) #ifdef ARCH_MM_MMU ret = copy_msghdr_from_user(&kmsg, (struct musl_msghdr *)msg, &uiov, - &msg_control, &msg_name, &buffer); + &msg_control, &msg_name, &buffer, RT_NULL, + RT_FALSE); - if (!ret) + if (ret < 0) { - kiov = kmsg.msg_iov; + return ret; + } + kiov = kmsg.msg_iov; - for (index = 0; index < kmsg.msg_iovlen; ++index) - { - lwp_get_from_user(kiov->iov_base, uiov->iov_base, kiov->iov_len); + for (index = 0; index < kmsg.msg_iovlen; ++index) + { + lwp_get_from_user(kiov->iov_base, uiov->iov_base, kiov->iov_len); - ++kiov; - ++uiov; - } + ++kiov; + ++uiov; + } - if (msg_control != RT_NULL && kmsg.msg_controllen != 0) - { - lwp_get_from_user(kmsg.msg_control, msg_control, - kmsg.msg_controllen); - cmsg_level_muslc_2_lwip(&kmsg); - } - if (msg_name != RT_NULL && kmsg.msg_namelen != 0) - { - union lwp_sockaddr_buffer kernel_address; + if (msg_control != RT_NULL && kmsg.msg_controllen != 0) + { + lwp_get_from_user(kmsg.msg_control, msg_control, + kmsg.msg_controllen); + cmsg_level_muslc_2_lwip(&kmsg); + } + if (msg_name != RT_NULL && kmsg.msg_namelen != 0) + { + union lwp_sockaddr_buffer kernel_address; - ret = lwp_sockaddr_from_user(&kernel_address, - (struct musl_sockaddr *)msg_name, - kmsg.msg_namelen); - if (ret < 0) - { - kmem_put(buffer); - kmem_put(kmsg.msg_iov); - return ret; - } - rt_memcpy(kmsg.msg_name, &kernel_address, kmsg.msg_namelen); + ret = lwp_sockaddr_from_user(&kernel_address, + (struct musl_sockaddr *)msg_name, + kmsg.msg_namelen); + if (ret < 0) + { + kmem_put(buffer); + kmem_put(kmsg.msg_iov); + return ret; } + rt_memcpy(kmsg.msg_name, &kernel_address, kmsg.msg_namelen); + } - ret = sendmsg(socket, &kmsg, flgs); - - kmem_put(buffer); - kmem_put(kmsg.msg_iov); + ret = sendmsg(socket, &kmsg, flgs); + if (ret < 0) + { + error = GET_ERRNO(); } + + kmem_put(buffer); + kmem_put(kmsg.msg_iov); #else kmsg.msg_name = msg->msg_name; kmsg.msg_namelen = msg->msg_namelen; @@ -6246,10 +6310,18 @@ sysret_t sys_sendmsg(int socket, const struct musl_msghdr *msg, int flags) kmsg.msg_flags = msg->msg_flags; ret = sendmsg(socket, &kmsg, flgs); + if (ret < 0) + { + error = GET_ERRNO(); + } #endif /* ARCH_MM_MMU */ - return (ret < 0 ? GET_ERRNO() : ret); + if (ret >= 0) + { + return ret; + } + return error != 0 ? error : ret; } /** @@ -6530,7 +6602,9 @@ sysret_t sys_socketpair(int domain, int type, int protocol, int fd[2]) */ sysret_t sys_closesocket(int socket) { - return closesocket(socket); + sysret_t ret; + ret = closesocket(socket); + return ret; } #endif @@ -10619,6 +10693,14 @@ sysret_t sys_chmod(const char *pathname, mode_t mode) return (ret < 0 ? GET_ERRNO() : ret); } +sysret_t sys_fchmod(int fd, mode_t mode) +{ + int ret; + + ret = fchmod(fd, mode); + return (ret < 0 ? GET_ERRNO() : ret); +} + /** * @brief Change the ownership of a file or directory. * @@ -11497,6 +11579,7 @@ const static struct rt_syscall_def func_table[] = { SYSCALL_SIGN(sys_getppid), SYSCALL_SIGN(sys_fchdir), SYSCALL_SIGN(sys_chown), + SYSCALL_SIGN(sys_fchmod), }; const void *lwp_get_sys_api(rt_uint32_t number) From 4bea94575b2a61222df4d2b35d57ccff16d187f8 Mon Sep 17 00:00:00 2001 From: bernard Date: Sun, 23 Aug 2026 07:44:06 +0800 Subject: [PATCH 4/9] [drivers][virtio] Depend VirtIO net on LWIP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将 VirtIO Net 的依赖从 RT_USING_ETHERNET 调整为 RT_USING_LWIP。 避免 SMART QEMU 在未启用以太网抽象时无法选择网卡。 --- components/drivers/virtio/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/drivers/virtio/Kconfig b/components/drivers/virtio/Kconfig index afa92546f388..37ed49d8767f 100644 --- a/components/drivers/virtio/Kconfig +++ b/components/drivers/virtio/Kconfig @@ -21,7 +21,7 @@ config RT_VIRTIO_TRANSPORT_PCI config RT_VIRTIO_NET bool "VirtIO Net" depends on RT_USING_VIRTIO - depends on RT_USING_ETHERNET + depends on RT_USING_LWIP default y config RT_VIRTIO_BLK From 53409929214aa3185bf1aba2e5abc8cbeb74740e Mon Sep 17 00:00:00 2001 From: bernard Date: Sun, 23 Aug 2026 10:13:09 +0800 Subject: [PATCH 5/9] [dfs][posix] Fix EFAULT, tmpfs setattr, and chmod bits NULL read/write buffers now return -EFAULT. tmpfs setattr uses the vnode-backed object after unlink, keeps S_ISUID/S_ISGID/S_ISVTX, and the tmpfs utest checks ENAMETOOLONG plus post-unlink fchmod. --- .../dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c | 38 ++++++++++++++----- .../dfs_v2/filesystems/tmpfs/utest/tmpfs.c | 17 ++++++++- components/dfs/dfs_v2/src/dfs_posix.c | 4 +- 3 files changed, 46 insertions(+), 13 deletions(-) diff --git a/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c b/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c index 0f1d42e9f860..96c77c10041d 100644 --- a/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c +++ b/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c @@ -538,15 +538,32 @@ static int dfs_tmpfs_open(struct dfs_file *file) return 0; } -static int dfs_tmpfs_stat(struct dfs_dentry *dentry, struct stat *st) +static struct tmpfs_file *_tmpfs_file_from_dentry(struct dfs_dentry *dentry) { rt_size_t size; - struct tmpfs_file *d_file; struct tmpfs_sb *superblock; + struct tmpfs_file *d_file; + + if (dentry == RT_NULL || dentry->mnt == RT_NULL) + { + return RT_NULL; + } + + if (dentry->vnode != RT_NULL && dentry->vnode->data != RT_NULL) + { + return (struct tmpfs_file *)dentry->vnode->data; + } superblock = (struct tmpfs_sb *)dentry->mnt->data; d_file = dfs_tmpfs_lookup(superblock, dentry->pathname, &size); + return d_file; +} + +static int dfs_tmpfs_stat(struct dfs_dentry *dentry, struct stat *st) +{ + struct tmpfs_file *d_file; + d_file = _tmpfs_file_from_dentry(dentry); if (d_file == NULL) { return -ENOENT; @@ -566,7 +583,6 @@ static int dfs_tmpfs_stat(struct dfs_dentry *dentry, struct stat *st) static int dfs_tmpfs_setattr(struct dfs_dentry *dentry, struct dfs_attr *attr) { - rt_size_t size; struct tmpfs_file *d_file; struct tmpfs_sb *superblock; @@ -575,16 +591,17 @@ static int dfs_tmpfs_setattr(struct dfs_dentry *dentry, struct dfs_attr *attr) return -EINVAL; } - superblock = (struct tmpfs_sb *)dentry->mnt->data; - d_file = dfs_tmpfs_lookup(superblock, dentry->pathname, &size); + d_file = _tmpfs_file_from_dentry(dentry); if (d_file == RT_NULL) { return -ENOENT; } + superblock = (struct tmpfs_sb *)dentry->mnt->data; if (attr->ia_valid & ATTR_MODE_SET) { - mode_t permissions = attr->st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); + mode_t permissions = attr->st_mode & + (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX); rt_spin_lock(&superblock->lock); d_file->mode = (d_file->mode & S_IFMT) | permissions; @@ -902,7 +919,8 @@ static struct dfs_vnode *dfs_tmpfs_create_vnode(struct dfs_dentry *dentry, int t if (type == FT_DIRECTORY) { d_file->type = TMPFS_TYPE_DIR; - d_file->mode = S_IFDIR | (mode & (S_IRWXU | S_IRWXG | S_IRWXO)); + d_file->mode = S_IFDIR | (mode & (S_IRWXU | S_IRWXG | S_IRWXO | + S_ISUID | S_ISGID | S_ISVTX)); vnode->mode = d_file->mode; vnode->type = FT_DIRECTORY; } @@ -910,14 +928,16 @@ static struct dfs_vnode *dfs_tmpfs_create_vnode(struct dfs_dentry *dentry, int t (type == FT_REGULAR && S_ISSOCK(mode))) { d_file->type = TMPFS_TYPE_SOCKET; - d_file->mode = S_IFSOCK | (mode & (S_IRWXU | S_IRWXG | S_IRWXO)); + d_file->mode = S_IFSOCK | (mode & (S_IRWXU | S_IRWXG | S_IRWXO | + S_ISUID | S_ISGID | S_ISVTX)); vnode->mode = d_file->mode; vnode->type = FT_SOCKET; } else { d_file->type = TMPFS_TYPE_FILE; - d_file->mode = S_IFREG | (mode & (S_IRWXU | S_IRWXG | S_IRWXO)); + d_file->mode = S_IFREG | (mode & (S_IRWXU | S_IRWXG | S_IRWXO | + S_ISUID | S_ISGID | S_ISVTX)); vnode->mode = d_file->mode; vnode->type = FT_REGULAR; #ifdef RT_USING_PAGECACHE diff --git a/components/dfs/dfs_v2/filesystems/tmpfs/utest/tmpfs.c b/components/dfs/dfs_v2/filesystems/tmpfs/utest/tmpfs.c index 502c472718c8..0e5a5ec6072b 100644 --- a/components/dfs/dfs_v2/filesystems/tmpfs/utest/tmpfs.c +++ b/components/dfs/dfs_v2/filesystems/tmpfs/utest/tmpfs.c @@ -13,10 +13,10 @@ #include #include #include +#include #include #include "utest.h" #include "utest_assert.h" -#include "common.h" void run_copy() { @@ -96,6 +96,7 @@ static void run_too_long_name_reject(void) fd = open(path, O_CREAT | O_RDWR, 0); uassert_int_equal(fd, -1); + uassert_int_equal(errno, -ENAMETOOLONG); fd = open("/tmp/tmpfs-after-long-name", O_CREAT | O_RDWR | O_TRUNC, 0); uassert_true(fd >= 0); @@ -118,12 +119,24 @@ static void run_mode_change(void) uassert_true(fd >= 0); uassert_int_equal(fstat(fd, &st), 0); uassert_int_equal(st.st_mode & 0777, 0700); - uassert_int_equal(fchmod(fd, 0400), 0); + uassert_int_equal(fchmod(fd, 0400 | S_ISVTX), 0); uassert_int_equal(fstat(fd, &st), 0); uassert_int_equal(st.st_mode & 0777, 0400); + uassert_true((st.st_mode & S_ISVTX) != 0); + uassert_int_equal(fchmod(fd, 0400), 0); + uassert_int_equal(fstat(fd, &st), 0); + uassert_int_equal(st.st_mode & S_ISVTX, 0); + + uassert_int_equal(unlink(path), 0); + uassert_int_equal(fchmod(fd, 0600), 0); + uassert_int_equal(fstat(fd, &st), 0); + uassert_int_equal(st.st_mode & 0777, 0600); uassert_int_equal(close(fd), 0); errno = 0; + fd = open(path, O_CREAT | O_EXCL | O_RDWR, 0400); + uassert_true(fd >= 0); + uassert_int_equal(close(fd), 0); uassert_int_equal(open(path, O_WRONLY, 0), -1); uassert_int_equal(errno, -EACCES); uassert_int_equal(unlink(path), 0); diff --git a/components/dfs/dfs_v2/src/dfs_posix.c b/components/dfs/dfs_v2/src/dfs_posix.c index 77410c4dd55e..d44d7184d4a1 100644 --- a/components/dfs/dfs_v2/src/dfs_posix.c +++ b/components/dfs/dfs_v2/src/dfs_posix.c @@ -339,7 +339,7 @@ ssize_t read(int fd, void *buf, size_t len) if (buf == NULL) { - rt_set_errno(-EBADF); + rt_set_errno(-EFAULT); return -1; } @@ -389,7 +389,7 @@ ssize_t write(int fd, const void *buf, size_t len) if (buf == NULL) { - rt_set_errno(-EBADF); + rt_set_errno(-EFAULT); return -1; } From 815343353ae08449eeb024ba668deeb25a99be86 Mon Sep 17 00:00:00 2001 From: bernard Date: Sun, 23 Aug 2026 10:13:24 +0800 Subject: [PATCH 6/9] [libc][posix] Wake epoll waiters on OOM If waiter allocation fails, mark the fd with POLLERR and wake epoll_wait() instead of silently dropping the watch. --- components/libc/posix/io/epoll/epoll.c | 31 ++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/components/libc/posix/io/epoll/epoll.c b/components/libc/posix/io/epoll/epoll.c index 9aa11e2d87fb..b6605f6b0889 100644 --- a/components/libc/posix/io/epoll/epoll.c +++ b/components/libc/posix/io/epoll/epoll.c @@ -57,6 +57,7 @@ struct rt_fd_list struct rt_epoll_waiter *waiters; /**< Wait nodes registered by poll */ int exclusive; /**< Indicates if the event is exclusive */ rt_bool_t is_rdl_node; /**< Indicates if the node is in the ready list */ + rt_bool_t waiter_oom; /**< Waiter allocation failed; report POLLERR */ int fd; /**< File descriptor */ struct rt_fd_list *next; /**< Pointer to the next file descriptor list */ rt_slist_t rdl_node; /**< Ready list node */ @@ -306,6 +307,29 @@ static void epoll_wqueue_add_callback(rt_wqueue_t *wq, rt_pollreq_t *req) waiter = (struct rt_epoll_waiter *)rt_calloc(1, sizeof(*waiter)); if (waiter == RT_NULL) { + rt_base_t level; + + /* + * Do not leave the fd unwatched: mark it ready with POLLERR so + * epoll_wait() wakes instead of sleeping on an incomplete set. + */ + if (ep) + { + level = rt_spin_lock_irqsave(&ep->spinlock); + if (fdlist->is_rdl_node == RT_FALSE) + { + rt_slist_append(&ep->rdl_head, &fdlist->rdl_node); + fdlist->exclusive = 0; + fdlist->is_rdl_node = RT_TRUE; + ep->eventpoll_num++; + } + fdlist->waiter_oom = RT_TRUE; + fdlist->revents |= POLLERR; + fdlist->epev.events |= POLLERR; + ep->status = RT_EPOLL_STAT_TRIG; + rt_wqueue_wakeup(&ep->epoll_read, (void *)POLLIN); + rt_spin_unlock_irqrestore(&ep->spinlock, level); + } return; } waiter->fdlist = fdlist; @@ -413,6 +437,7 @@ static int epoll_epf_init(int fd) ep->fdlist->waiters = RT_NULL; ep->fdlist->exclusive = 0; ep->fdlist->is_rdl_node = RT_FALSE; + ep->fdlist->waiter_oom = RT_FALSE; dfs_vnode_init(df->vnode, FT_REGULAR, &epoll_fops); df->vnode->data = ep; rt_slist_init(&ep->fdlist->rdl_node); @@ -525,6 +550,7 @@ static int epoll_ctl_add(struct dfs_file *df, int fd, struct epoll_event *event) fdlist->waiters = RT_NULL; fdlist->exclusive = 0; fdlist->is_rdl_node = RT_FALSE; + fdlist->waiter_oom = RT_FALSE; fdlist->req._proc = epoll_wqueue_add_callback; fdlist->revents = event->events; rt_mutex_take(&ep->lock, RT_WAITING_FOREVER); @@ -823,6 +849,11 @@ static int epoll_get_event(struct rt_fd_list *fl, rt_pollreq_t *req) } } + if (fl->waiter_oom) + { + mask |= POLLERR; + } + return mask; } From 37808054ed69556daaf0c06247bf384da460f38b Mon Sep 17 00:00:00 2001 From: bernard Date: Sun, 23 Aug 2026 10:13:24 +0800 Subject: [PATCH 7/9] [dfs][procfs] Fix Linux-compatible procfs fields Grow maps without a 256 VMA cap, skip RSS walks for maps, report matching status text, real RSS/statm, MemAvailable after page-pool free pages, load averages plus last PID, cumulative processes, RV32 cpuinfo, and per-character mount escaping. --- .../dfs_v2/filesystems/procfs/proc_cpuinfo.c | 4 + .../dfs_v2/filesystems/procfs/proc_loadavg.c | 69 ++++++- .../dfs_v2/filesystems/procfs/proc_meminfo.c | 2 +- .../dfs_v2/filesystems/procfs/proc_mounts.c | 42 ++-- .../dfs/dfs_v2/filesystems/procfs/proc_pid.c | 183 +++++++++++++++--- .../dfs/dfs_v2/filesystems/procfs/proc_stat.c | 2 +- components/lwp/lwp_pid.c | 24 +++ components/lwp/lwp_pid.h | 2 + 8 files changed, 279 insertions(+), 49 deletions(-) diff --git a/components/dfs/dfs_v2/filesystems/procfs/proc_cpuinfo.c b/components/dfs/dfs_v2/filesystems/procfs/proc_cpuinfo.c index ca3a8dc48acb..094fa39e12cf 100644 --- a/components/dfs/dfs_v2/filesystems/procfs/proc_cpuinfo.c +++ b/components/dfs/dfs_v2/filesystems/procfs/proc_cpuinfo.c @@ -76,7 +76,11 @@ static int seq_show(struct dfs_seq_file *seq, void *data) dfs_seq_printf(seq, "CPU revision\t: %lu\n", (unsigned long)revision); #elif defined(__riscv) dfs_seq_puts(seq, "model name\t: RISC-V Generic\n"); +#if defined(__riscv_xlen) && (__riscv_xlen == 32) + dfs_seq_puts(seq, "isa\t\t: rv32\n"); +#else dfs_seq_puts(seq, "isa\t\t: rv64\n"); +#endif #else dfs_seq_puts(seq, "model name\t: RT-Thread Generic CPU\n"); #endif diff --git a/components/dfs/dfs_v2/filesystems/procfs/proc_loadavg.c b/components/dfs/dfs_v2/filesystems/procfs/proc_loadavg.c index acb6ee35b563..44e95ff5f12b 100644 --- a/components/dfs/dfs_v2/filesystems/procfs/proc_loadavg.c +++ b/components/dfs/dfs_v2/filesystems/procfs/proc_loadavg.c @@ -19,10 +19,8 @@ #include #ifdef RT_USING_SMART -#include "lwp_pid.h" -#ifdef RT_USING_MUSLLIBC #include "lwp.h" -#endif +#include "lwp_pid.h" #endif #ifdef RT_USING_SMART @@ -32,6 +30,18 @@ struct loadavg_count int runnable; }; +/* Fixed-point load averages, 11 bits of fraction like Linux FSHIFT. */ +#define PROC_LOAD_FSHIFT 11 +#define PROC_LOAD_FIXED_1 (1UL << PROC_LOAD_FSHIFT) +#define PROC_LOAD_EXP_1 1884UL /* 1 / exp(5s / 60s) * 2048 */ +#define PROC_LOAD_EXP_5 2014UL /* 1 / exp(5s / 300s) * 2048 */ +#define PROC_LOAD_EXP_15 2037UL /* 1 / exp(5s / 900s) * 2048 */ + +static unsigned long load_1 = 0; +static unsigned long load_5 = 0; +static unsigned long load_15 = 0; +static rt_tick_t load_last_tick = 0; + static int loadavg_count_pid(pid_t pid, void *arg) { struct loadavg_count *count = (struct loadavg_count *)arg; @@ -42,7 +52,6 @@ static int loadavg_count_pid(pid_t pid, void *arg) return 0; } count->total++; -#ifdef RT_USING_MUSLLIBC { rt_list_t *node; @@ -50,7 +59,9 @@ static int loadavg_count_pid(pid_t pid, void *arg) while (node != &lwp->t_grp) { rt_thread_t thread = rt_list_entry(node, struct rt_thread, sibling); - if (RT_SCHED_CTX(thread).stat == RT_THREAD_RUNNING) + int stat = RT_SCHED_CTX(thread).stat & RT_THREAD_STAT_MASK; + + if (stat == RT_THREAD_RUNNING || stat == RT_THREAD_READY) { count->runnable++; break; @@ -58,18 +69,62 @@ static int loadavg_count_pid(pid_t pid, void *arg) node = node->next; } } -#endif return 0; } + +static void loadavg_fold(unsigned long active) +{ + load_1 = load_1 * PROC_LOAD_EXP_1 / PROC_LOAD_FIXED_1 + + active * (PROC_LOAD_FIXED_1 - PROC_LOAD_EXP_1) / PROC_LOAD_FIXED_1; + load_5 = load_5 * PROC_LOAD_EXP_5 / PROC_LOAD_FIXED_1 + + active * (PROC_LOAD_FIXED_1 - PROC_LOAD_EXP_5) / PROC_LOAD_FIXED_1; + load_15 = load_15 * PROC_LOAD_EXP_15 / PROC_LOAD_FIXED_1 + + active * (PROC_LOAD_FIXED_1 - PROC_LOAD_EXP_15) / PROC_LOAD_FIXED_1; +} + +static void loadavg_update(unsigned long active) +{ + rt_tick_t now = rt_tick_get(); + rt_tick_t period = RT_TICK_PER_SECOND * 5; + + if (load_last_tick == 0) + { + load_1 = load_5 = load_15 = active; + load_last_tick = now; + return; + } + + while ((rt_tick_t)(now - load_last_tick) >= period) + { + loadavg_fold(active); + load_last_tick += period; + } +} + +static void loadavg_print(struct dfs_seq_file *seq, unsigned long load) +{ + unsigned long integer = load >> PROC_LOAD_FSHIFT; + unsigned long frac = ((load & (PROC_LOAD_FIXED_1 - 1)) * 100) >> PROC_LOAD_FSHIFT; + + dfs_seq_printf(seq, "%lu.%02lu", integer, frac); +} #endif static int single_show(struct dfs_seq_file *seq, void *data) { + RT_UNUSED(data); #ifdef RT_USING_SMART struct loadavg_count count = { 0, 0 }; lwp_pid_for_each(loadavg_count_pid, &count); - dfs_seq_printf(seq, "0.00 0.00 0.00 %d/%d 0\n", count.runnable, count.total); + loadavg_update(((unsigned long)count.runnable) << PROC_LOAD_FSHIFT); + loadavg_print(seq, load_1); + dfs_seq_puts(seq, " "); + loadavg_print(seq, load_5); + dfs_seq_puts(seq, " "); + loadavg_print(seq, load_15); + dfs_seq_printf(seq, " %d/%d %d\n", count.runnable, count.total, + (int)lwp_pid_get_last()); #else dfs_seq_puts(seq, "0.00 0.00 0.00 0/0 0\n"); #endif diff --git a/components/dfs/dfs_v2/filesystems/procfs/proc_meminfo.c b/components/dfs/dfs_v2/filesystems/procfs/proc_meminfo.c index b1617f8bfd77..1d8048b06b7f 100644 --- a/components/dfs/dfs_v2/filesystems/procfs/proc_meminfo.c +++ b/components/dfs/dfs_v2/filesystems/procfs/proc_meminfo.c @@ -35,12 +35,12 @@ static int single_show(struct dfs_seq_file *seq, void *data) total_freed = total_freed + total - used; dfs_seq_printf(seq, "%-16s%8lu kB\n", "MemMaxUsed:", (unsigned long)(max_used / 1024)); - dfs_seq_printf(seq, "%-16s%8lu kB\n", "MemAvailable:", (unsigned long)((total - used) / 1024)); rt_page_get_info(&total, &freed); total_sum = total_sum + total * RT_MM_PAGE_SIZE; total_freed = total_freed + freed * RT_MM_PAGE_SIZE; + dfs_seq_printf(seq, "%-16s%8lu kB\n", "MemAvailable:", (unsigned long)(total_freed / 1024)); dfs_seq_printf(seq, "%-16s%8lu kB\n", "MemTotal:", (unsigned long)(total_sum / 1024)); dfs_seq_printf(seq, "%-16s%8lu kB\n", "MemFree:", (unsigned long)(total_freed / 1024)); dfs_seq_printf(seq, "%-16s%8lu kB\n", "Buffers:", 0UL); diff --git a/components/dfs/dfs_v2/filesystems/procfs/proc_mounts.c b/components/dfs/dfs_v2/filesystems/procfs/proc_mounts.c index 1bb3b6a6bc6e..5d2e75f5a144 100644 --- a/components/dfs/dfs_v2/filesystems/procfs/proc_mounts.c +++ b/components/dfs/dfs_v2/filesystems/procfs/proc_mounts.c @@ -29,35 +29,43 @@ static void mnt_escape(const char *source, char *target, rt_size_t target_size) return; } - while (*cursor != '\0' && offset + 4 < target_size) + while (*cursor != '\0') { + const char *escape = RT_NULL; + rt_size_t need = 1; + if (*cursor == ' ') { - target[offset++] = '\\'; - target[offset++] = '0'; - target[offset++] = '4'; - target[offset++] = '0'; + escape = "\\040"; + need = 4; } else if (*cursor == '\t') { - target[offset++] = '\\'; - target[offset++] = '0'; - target[offset++] = '1'; - target[offset++] = '1'; + escape = "\\011"; + need = 4; } else if (*cursor == '\n') { - target[offset++] = '\\'; - target[offset++] = '0'; - target[offset++] = '1'; - target[offset++] = '2'; + escape = "\\012"; + need = 4; } else if (*cursor == '\\') { - target[offset++] = '\\'; - target[offset++] = '1'; - target[offset++] = '3'; - target[offset++] = '4'; + escape = "\\134"; + need = 4; + } + + if (offset + need >= target_size) + { + break; + } + + if (escape) + { + target[offset++] = escape[0]; + target[offset++] = escape[1]; + target[offset++] = escape[2]; + target[offset++] = escape[3]; } else { diff --git a/components/dfs/dfs_v2/filesystems/procfs/proc_pid.c b/components/dfs/dfs_v2/filesystems/procfs/proc_pid.c index 9212351ac419..a3b0422c3880 100644 --- a/components/dfs/dfs_v2/filesystems/procfs/proc_pid.c +++ b/components/dfs/dfs_v2/filesystems/procfs/proc_pid.c @@ -46,17 +46,76 @@ struct pid_dentry static char stat_transform(int __stat) { - switch (__stat) + int stat = __stat & RT_THREAD_STAT_MASK; + + if (stat == RT_THREAD_RUNNING) + { + return 'R'; + } + if (stat == RT_THREAD_READY) { - case RT_THREAD_RUNNING: return 'R'; + } + if (stat == RT_THREAD_CLOSE) + { + return 'Z'; + } + if (stat & RT_THREAD_SUSPEND_MASK) + { + return 'S'; + } + return 'T'; +} + +static const char *proc_state_desc(char state) +{ + switch (state) + { + case 'R': + return "running"; + case 'S': + return "sleeping"; + case 'Z': + return "zombie"; + case 'T': + return "stopped"; default: - return 'T'; + return "unknown"; + } +} + +static char proc_lwp_state(struct rt_lwp *lwp) +{ + rt_list_t *node; + char state = 'S'; + + if (!lwp || lwp->terminated) + { + return 'Z'; + } + + node = lwp->t_grp.next; + while (node != &lwp->t_grp) + { + rt_thread_t thread = rt_list_entry(node, struct rt_thread, sibling); + char thread_state = stat_transform(RT_SCHED_CTX(thread).stat); + + if (thread_state == 'R') + { + return 'R'; + } + if (thread_state == 'T') + { + state = 'T'; + } + node = node->next; } + + return state; } #ifdef ARCH_MM_MMU -#define PROC_MAP_MAX 256 +#define PROC_MAP_GROW 32 struct proc_map_entry { @@ -75,6 +134,8 @@ struct proc_map_context struct rt_lwp *lwp; struct proc_map_entry *entries; rt_size_t count; + rt_size_t capacity; + rt_bool_t count_resident; rt_bool_t smaps; }; @@ -104,17 +165,68 @@ static void proc_map_name(rt_varea_t varea, struct proc_map_entry *entry) entry->name[sizeof(entry->name) - 1] = '\0'; } +static int proc_map_grow(struct proc_map_context *context) +{ + struct proc_map_entry *entries; + rt_size_t capacity; + + if (context->count < context->capacity) + { + return 0; + } + + capacity = context->capacity ? context->capacity * 2 : PROC_MAP_GROW; + entries = rt_realloc(context->entries, capacity * sizeof(*entries)); + if (!entries) + { + return -RT_ENOMEM; + } + + rt_memset(entries + context->capacity, 0, + (capacity - context->capacity) * sizeof(*entries)); + context->entries = entries; + context->capacity = capacity; + return 0; +} + +static rt_size_t proc_map_count_resident(rt_varea_t varea) +{ + uintptr_t address; + uintptr_t end; + rt_size_t resident_pages = 0; + + if (!varea || varea->size == 0) + { + return 0; + } + + address = (uintptr_t)varea->start; + end = address + varea->size; + while (address < end) + { + if (rt_hw_mmu_v2p(varea->aspace, (void *)address) != ARCH_MAP_FAILED) + { + resident_pages++; + } + address += ARCH_PAGE_SIZE; + } + + return resident_pages; +} + static int proc_map_collect(rt_varea_t varea, void *arg) { struct proc_map_context *context = (struct proc_map_context *)arg; struct proc_map_entry *entry; - uintptr_t address; - uintptr_t end; - if (context->count >= PROC_MAP_MAX || !varea || varea->size == 0) + if (!varea || varea->size == 0) { return 0; } + if (proc_map_grow(context) != 0) + { + return -RT_ENOMEM; + } entry = &context->entries[context->count++]; entry->start = (uintptr_t)varea->start; @@ -124,20 +236,36 @@ static int proc_map_collect(rt_varea_t varea, void *arg) proc_map_permissions(varea, entry); proc_map_name(varea, entry); - address = entry->start; - end = entry->end; - while (address < end) + if (context->count_resident) { - if (rt_hw_mmu_v2p(varea->aspace, (void *)address) != ARCH_MAP_FAILED) - { - entry->resident_pages++; - } - address += ARCH_PAGE_SIZE; + entry->resident_pages = proc_map_count_resident(varea); } return 0; } +static rt_size_t proc_lwp_resident_pages(struct rt_lwp *lwp) +{ + rt_varea_t varea; + rt_size_t resident_pages = 0; + + if (!lwp || !lwp->aspace) + { + return 0; + } + + RD_LOCK(lwp->aspace); + varea = ASPACE_VAREA_FIRST(lwp->aspace); + while (varea) + { + resident_pages += proc_map_count_resident(varea); + varea = ASPACE_VAREA_NEXT(varea); + } + RD_UNLOCK(lwp->aspace); + + return resident_pages; +} + static void proc_map_context_free(struct proc_map_context *context) { if (context) @@ -244,7 +372,9 @@ static int proc_maps_open(struct dfs_file *file) return -RT_ENOENT; } context->smaps = !rt_strcmp(dentry->name, "smaps"); - context->entries = rt_calloc(PROC_MAP_MAX, sizeof(*context->entries)); + context->count_resident = context->smaps; + context->capacity = PROC_MAP_GROW; + context->entries = rt_calloc(context->capacity, sizeof(*context->entries)); if (!context->entries) { proc_map_context_free(context); @@ -255,7 +385,12 @@ static int proc_maps_open(struct dfs_file *file) varea = ASPACE_VAREA_FIRST(context->lwp->aspace); while (varea) { - proc_map_collect(varea, context); + if (proc_map_collect(varea, context) != 0) + { + RD_UNLOCK(context->lwp->aspace); + proc_map_context_free(context); + return -RT_ENOMEM; + } varea = ASPACE_VAREA_NEXT(varea); } RD_UNLOCK(context->lwp->aspace); @@ -447,6 +582,7 @@ static int proc_pid_status_show(struct dfs_seq_file *seq, void *data) struct proc_dentry *dentry = (struct proc_dentry *)seq->file->vnode->data; struct rt_lwp *lwp = lwp_from_pid_and_lock(dentry->pid); rt_size_t vm_size = 0; + rt_size_t vm_rss = 0; rt_size_t thread_count = 0; rt_list_t *node; char state; @@ -461,6 +597,7 @@ static int proc_pid_status_show(struct dfs_seq_file *seq, void *data) if (lwp->aspace) { vm_size = rt_aspace_count_vsz(lwp->aspace); + vm_rss = proc_lwp_resident_pages(lwp) * ARCH_PAGE_SIZE; } #endif node = lwp->t_grp.next; @@ -469,15 +606,15 @@ static int proc_pid_status_show(struct dfs_seq_file *seq, void *data) thread_count++; node = node->next; } - state = lwp->terminated ? 'Z' : 'S'; + state = proc_lwp_state(lwp); dfs_seq_printf(seq, "Name:\t%s\n", lwp->cmd); - dfs_seq_printf(seq, "State:\t%c (sleeping)\n", state); + dfs_seq_printf(seq, "State:\t%c (%s)\n", state, proc_state_desc(state)); dfs_seq_printf(seq, "Pid:\t%d\n", lwp->pid); dfs_seq_printf(seq, "PPid:\t%d\n", lwp->parent ? lwp->parent->pid : 0); dfs_seq_puts(seq, "Uid:\t0\t0\t0\t0\nGid:\t0\t0\t0\t0\n"); dfs_seq_printf(seq, "Threads:\t%lu\n", (unsigned long)thread_count); dfs_seq_printf(seq, "VmSize:\t%lu kB\n", (unsigned long)(vm_size / 1024)); - dfs_seq_printf(seq, "VmRSS:\t%lu kB\n", (unsigned long)(vm_size / 1024)); + dfs_seq_printf(seq, "VmRSS:\t%lu kB\n", (unsigned long)(vm_rss / 1024)); dfs_seq_puts(seq, "VmData:\t0 kB\nVmStk:\t0 kB\nVmExe:\t0 kB\nVmLib:\t0 kB\n"); lwp_from_pid_release_lock(lwp); return 0; @@ -488,6 +625,7 @@ static int proc_pid_statm_show(struct dfs_seq_file *seq, void *data) struct proc_dentry *dentry = (struct proc_dentry *)seq->file->vnode->data; struct rt_lwp *lwp = lwp_from_pid_and_lock(dentry->pid); rt_size_t size_pages = 0; + rt_size_t resident_pages = 0; rt_size_t text_pages = 0; rt_size_t data_pages = 0; @@ -501,14 +639,13 @@ static int proc_pid_statm_show(struct dfs_seq_file *seq, void *data) if (lwp->aspace) { size_pages = (rt_aspace_count_vsz(lwp->aspace) + ARCH_PAGE_SIZE - 1) / ARCH_PAGE_SIZE; + resident_pages = proc_lwp_resident_pages(lwp); } -#endif -#ifdef ARCH_MM_MMU text_pages = (lwp->text_size + ARCH_PAGE_SIZE - 1) / ARCH_PAGE_SIZE; data_pages = (lwp->data_size + ARCH_PAGE_SIZE - 1) / ARCH_PAGE_SIZE; #endif dfs_seq_printf(seq, "%lu %lu 0 %lu 0 %lu 0\n", - (unsigned long)size_pages, (unsigned long)size_pages, + (unsigned long)size_pages, (unsigned long)resident_pages, (unsigned long)text_pages, (unsigned long)data_pages); lwp_from_pid_release_lock(lwp); return 0; diff --git a/components/dfs/dfs_v2/filesystems/procfs/proc_stat.c b/components/dfs/dfs_v2/filesystems/procfs/proc_stat.c index c24adb291e52..9b8a6a80c9ee 100644 --- a/components/dfs/dfs_v2/filesystems/procfs/proc_stat.c +++ b/components/dfs/dfs_v2/filesystems/procfs/proc_stat.c @@ -118,7 +118,7 @@ static int seq_show(struct dfs_seq_file *seq, void *data) { struct stat_process_count process_count = { 0, 0 }; lwp_pid_for_each(stat_process_count, &process_count); - dfs_seq_printf(seq, "processes %d\n", process_count.total); + dfs_seq_printf(seq, "processes %lu\n", lwp_pid_get_create_count()); dfs_seq_printf(seq, "procs_running %d\n", process_count.running); dfs_seq_puts(seq, "procs_blocked 0\n"); } diff --git a/components/lwp/lwp_pid.c b/components/lwp/lwp_pid.c index 12a4dde86f0a..1f0e09ae9cc8 100644 --- a/components/lwp/lwp_pid.c +++ b/components/lwp/lwp_pid.c @@ -68,6 +68,7 @@ static struct lwp_avl_struct *lwp_pid_free_head = RT_NULL; static int lwp_pid_ary_alloced = 0; static struct lwp_avl_struct *lwp_pid_root = RT_NULL; static pid_t current_pid = 0; +static unsigned long lwp_create_count = 0; static struct rt_mutex pid_mtx; static struct rt_wqueue _pid_emptyq; @@ -166,6 +167,28 @@ static int _before_cb(struct lwp_avl_struct *node, void *data) return param->cb(pid, param->data); } +unsigned long lwp_pid_get_create_count(void) +{ + unsigned long count; + + lwp_pid_lock_take(); + count = lwp_create_count; + lwp_pid_lock_release(); + + return count; +} + +pid_t lwp_pid_get_last(void) +{ + pid_t pid; + + lwp_pid_lock_take(); + pid = current_pid; + lwp_pid_lock_release(); + + return pid; +} + /** * @brief Iterate over all process IDs * @@ -694,6 +717,7 @@ rt_lwp_t lwp_create(rt_base_t flags) { new_lwp->pid = pid; lwp_pid_set_lwp_locked(pid, new_lwp); + lwp_create_count++; } lwp_pid_lock_release(); } diff --git a/components/lwp/lwp_pid.h b/components/lwp/lwp_pid.h index 5b85b3219772..a295137fbc7b 100644 --- a/components/lwp/lwp_pid.h +++ b/components/lwp/lwp_pid.h @@ -28,6 +28,8 @@ struct lwp_avl_struct *lwp_get_pid_ary(void); int lwp_pid_init(void); int lwp_pid_wait_for_empty(int wait_flags, rt_tick_t to); int lwp_pid_for_each(int (*cb)(pid_t pid, void *data), void *data); +unsigned long lwp_pid_get_create_count(void); +pid_t lwp_pid_get_last(void); void lwp_pid_put(struct rt_lwp *lwp); void lwp_pid_rollback(struct rt_lwp *lwp); void lwp_pid_lock_take(void); From 875d303366dfcdafe25e4e5c05008ceca92201e5 Mon Sep 17 00:00:00 2001 From: bernard Date: Sun, 23 Aug 2026 10:13:24 +0800 Subject: [PATCH 8/9] [dfs][utest] Add POSIX record-lock coverage Cover positive/negative l_len, overlapping GETLK conflicts, interrupted F_SETLKW, split/merge, and close/dup2 lock release on tmpfs. --- .../dfs_v2/filesystems/tmpfs/utest/Kconfig | 8 + .../dfs_v2/filesystems/tmpfs/utest/SConscript | 3 + .../filesystems/tmpfs/utest/tc_record_lock.c | 342 ++++++++++++++++++ 3 files changed, 353 insertions(+) create mode 100644 components/dfs/dfs_v2/filesystems/tmpfs/utest/tc_record_lock.c diff --git a/components/dfs/dfs_v2/filesystems/tmpfs/utest/Kconfig b/components/dfs/dfs_v2/filesystems/tmpfs/utest/Kconfig index c18979f750d4..96f36468f5f7 100644 --- a/components/dfs/dfs_v2/filesystems/tmpfs/utest/Kconfig +++ b/components/dfs/dfs_v2/filesystems/tmpfs/utest/Kconfig @@ -3,4 +3,12 @@ menu "Tmpfs Testcase" config RT_UTEST_TMPFS_CP bool "tmpfs cp test" default n + +config RT_UTEST_DFS_RECORD_LOCK + bool "DFS POSIX record lock test" + default n + depends on RT_USING_POSIX_FS && RT_USING_DFS_TMPFS + help + Enable POSIX advisory record-lock tests on tmpfs. + endmenu diff --git a/components/dfs/dfs_v2/filesystems/tmpfs/utest/SConscript b/components/dfs/dfs_v2/filesystems/tmpfs/utest/SConscript index 126fc7870b54..8449323d7499 100644 --- a/components/dfs/dfs_v2/filesystems/tmpfs/utest/SConscript +++ b/components/dfs/dfs_v2/filesystems/tmpfs/utest/SConscript @@ -8,6 +8,9 @@ CPPPATH = [cwd] if GetDepend(['RT_USING_SMART','RT_UTEST_TMPFS_CP']): src += ['tmpfs.c'] +if GetDepend(['RT_UTEST_DFS_RECORD_LOCK']): + src += ['tc_record_lock.c'] + group = DefineGroup('utestcases', src, depend = ['RT_USING_UTESTCASES'], CPPPATH = CPPPATH) Return('group') diff --git a/components/dfs/dfs_v2/filesystems/tmpfs/utest/tc_record_lock.c b/components/dfs/dfs_v2/filesystems/tmpfs/utest/tc_record_lock.c new file mode 100644 index 000000000000..ceb7214f697a --- /dev/null +++ b/components/dfs/dfs_v2/filesystems/tmpfs/utest/tc_record_lock.c @@ -0,0 +1,342 @@ +/* + * Copyright (c) 2006-2026 RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2026-08-23 Bernard POSIX record-lock coverage on tmpfs + */ + +#include +#include +#include +#include +#include + +#ifdef RT_USING_SIGNALS +#include +#endif + +#ifdef RT_USING_SMART +#include +rt_err_t sys_dup2(int oldfd, int newfd); +#endif + +#include "utest.h" +#include "utest_assert.h" + +#define LOCK_PATH "/tmp/record-lock-utest" +#define LOCK_PAYLOAD "0123456789abcdef" + +struct lock_waiter_arg +{ + struct flock fl; + int result; + int err; + rt_sem_t started; + rt_sem_t done; +}; + +static void fill_lock(struct flock *fl, short type, short whence, off_t start, off_t len) +{ + rt_memset(fl, 0, sizeof(*fl)); + fl->l_type = type; + fl->l_whence = whence; + fl->l_start = start; + fl->l_len = len; +} + +static int set_lock(int fd, int cmd, short type, short whence, off_t start, off_t len) +{ + struct flock fl; + + fill_lock(&fl, type, whence, start, len); + return fcntl(fd, cmd, &fl); +} + +static int get_lock(int fd, struct flock *fl, short type, short whence, off_t start, off_t len) +{ + fill_lock(fl, type, whence, start, len); + return fcntl(fd, F_GETLK, fl); +} + +static int create_lock_file(void) +{ + int fd; + + unlink(LOCK_PATH); + fd = open(LOCK_PATH, O_CREAT | O_RDWR | O_TRUNC, 0666); + uassert_true(fd >= 0); + uassert_int_equal(write(fd, LOCK_PAYLOAD, sizeof(LOCK_PAYLOAD) - 1), + sizeof(LOCK_PAYLOAD) - 1); + uassert_int_equal(lseek(fd, 0, SEEK_SET), 0); + return fd; +} + +#ifdef RT_USING_SMART +static struct rt_lwp *bind_owner(void) +{ + struct rt_lwp *lwp = lwp_create(LWP_CREATE_FLAG_ALLOC_PID); + + uassert_not_null(lwp); + rt_thread_self()->lwp = lwp; + return lwp; +} + +static void unbind_owner(struct rt_lwp *lwp) +{ + rt_thread_self()->lwp = RT_NULL; + if (lwp) + { + /* Drop create() + pid-table references; leftover pid slots are test-only. */ + lwp_ref_dec(lwp); + lwp_ref_dec(lwp); + } +} +#endif + +static void test_positive_negative_len(void) +{ + int fd = create_lock_file(); + struct flock fl; + + uassert_int_equal(set_lock(fd, F_SETLK, F_WRLCK, SEEK_SET, 0, 4), 0); + uassert_int_equal(get_lock(fd, &fl, F_WRLCK, SEEK_SET, 0, 4), 0); + uassert_int_equal(fl.l_type, F_UNLCK); + + uassert_int_equal(set_lock(fd, F_SETLK, F_UNLCK, SEEK_SET, 4, -4), 0); + uassert_int_equal(set_lock(fd, F_SETLK, F_RDLCK, SEEK_SET, 8, -4), 0); + uassert_int_equal(set_lock(fd, F_SETLK, F_UNLCK, SEEK_SET, 0, 0), 0); + uassert_int_equal(close(fd), 0); + uassert_int_equal(unlink(LOCK_PATH), 0); +} + +static void test_conflict_and_getlk(void) +{ +#ifdef RT_USING_SMART + int fd_a; + int fd_b; + struct flock fl; + struct rt_lwp *owner_a; + struct rt_lwp *owner_b; + + owner_a = bind_owner(); + fd_a = create_lock_file(); + uassert_int_equal(set_lock(fd_a, F_SETLK, F_WRLCK, SEEK_SET, 0, 8), 0); + + owner_b = lwp_create(LWP_CREATE_FLAG_ALLOC_PID); + uassert_not_null(owner_b); + rt_thread_self()->lwp = owner_b; + fd_b = open(LOCK_PATH, O_RDWR, 0); + uassert_true(fd_b >= 0); + + errno = 0; + uassert_int_equal(set_lock(fd_b, F_SETLK, F_RDLCK, SEEK_SET, 0, 4), -1); + uassert_int_equal(errno, -EAGAIN); + + uassert_int_equal(get_lock(fd_b, &fl, F_WRLCK, SEEK_SET, 0, 4), 0); + uassert_int_equal(fl.l_type, F_WRLCK); + uassert_int_equal(fl.l_whence, SEEK_SET); + uassert_int_equal(fl.l_start, 0); + uassert_int_equal(fl.l_len, 8); + uassert_int_equal(fl.l_pid, owner_a->pid); + + rt_thread_self()->lwp = owner_a; + uassert_int_equal(set_lock(fd_a, F_SETLK, F_UNLCK, SEEK_SET, 0, 0), 0); + + rt_thread_self()->lwp = owner_b; + uassert_int_equal(set_lock(fd_b, F_SETLK, F_RDLCK, SEEK_SET, 0, 4), 0); + uassert_int_equal(set_lock(fd_b, F_SETLK, F_UNLCK, SEEK_SET, 0, 0), 0); + uassert_int_equal(close(fd_b), 0); + + rt_thread_self()->lwp = owner_a; + uassert_int_equal(close(fd_a), 0); + unbind_owner(owner_a); + unbind_owner(owner_b); + uassert_int_equal(unlink(LOCK_PATH), 0); +#else + uassert_true(1); +#endif +} + +static void waiter_entry(void *parameter) +{ + struct lock_waiter_arg *arg = (struct lock_waiter_arg *)parameter; + int fd; +#ifdef RT_USING_SMART + struct rt_lwp *owner = bind_owner(); +#endif + +#ifdef RT_USING_SIGNALS + rt_signal_install(SIGUSR1, RT_NULL); + rt_signal_unmask(SIGUSR1); +#endif + fd = open(LOCK_PATH, O_RDWR, 0); + if (fd < 0) + { + arg->result = -1; + arg->err = errno; + rt_sem_release(arg->started); + rt_sem_release(arg->done); +#ifdef RT_USING_SMART + unbind_owner(owner); +#endif + return; + } + + rt_sem_release(arg->started); + arg->result = fcntl(fd, F_SETLKW, &arg->fl); + arg->err = errno; + close(fd); +#ifdef RT_USING_SMART + unbind_owner(owner); +#endif + rt_sem_release(arg->done); +} + +static void test_interrupted_setlkw(void) +{ +#if defined(RT_USING_SMART) && defined(RT_USING_SIGNALS) + int fd_a; + rt_thread_t waiter; + struct lock_waiter_arg arg; + struct rt_semaphore started; + struct rt_semaphore done; + struct rt_lwp *owner; + + owner = bind_owner(); + fd_a = create_lock_file(); + uassert_int_equal(set_lock(fd_a, F_SETLK, F_WRLCK, SEEK_SET, 0, 4), 0); + + rt_sem_init(&started, "rlks", 0, RT_IPC_FLAG_FIFO); + rt_sem_init(&done, "rlkw", 0, RT_IPC_FLAG_FIFO); + fill_lock(&arg.fl, F_WRLCK, SEEK_SET, 0, 4); + arg.result = 0; + arg.err = 0; + arg.started = &started; + arg.done = &done; + + waiter = rt_thread_create("rlkw", waiter_entry, &arg, 4096, + RT_SCHED_PRIV(rt_thread_self()).current_priority + 1, 10); + uassert_not_null(waiter); + rt_thread_startup(waiter); + uassert_int_equal(rt_sem_take(&started, RT_TICK_PER_SECOND), RT_EOK); + rt_thread_mdelay(10); + uassert_int_equal(rt_thread_kill(waiter, SIGUSR1), RT_EOK); + uassert_int_equal(rt_sem_take(&done, RT_TICK_PER_SECOND), RT_EOK); + uassert_int_equal(arg.result, -1); + uassert_int_equal(arg.err, -EINTR); + + uassert_int_equal(set_lock(fd_a, F_SETLK, F_UNLCK, SEEK_SET, 0, 0), 0); + uassert_int_equal(close(fd_a), 0); + unbind_owner(owner); + rt_sem_detach(&started); + rt_sem_detach(&done); + uassert_int_equal(unlink(LOCK_PATH), 0); +#else + uassert_true(1); +#endif +} + +static void test_split_merge(void) +{ + int fd = create_lock_file(); + struct flock fl; + + uassert_int_equal(set_lock(fd, F_SETLK, F_WRLCK, SEEK_SET, 0, 16), 0); + uassert_int_equal(set_lock(fd, F_SETLK, F_UNLCK, SEEK_SET, 4, 4), 0); + uassert_int_equal(set_lock(fd, F_SETLK, F_WRLCK, SEEK_SET, 4, 4), 0); + + uassert_int_equal(get_lock(fd, &fl, F_WRLCK, SEEK_SET, 0, 16), 0); + uassert_int_equal(fl.l_type, F_UNLCK); + + uassert_int_equal(set_lock(fd, F_SETLK, F_UNLCK, SEEK_SET, 0, 0), 0); + uassert_int_equal(close(fd), 0); + uassert_int_equal(unlink(LOCK_PATH), 0); +} + +static void test_close_and_dup_release(void) +{ +#ifdef RT_USING_SMART + int fd_a; + int fd_b; + int fd_replace; + struct flock fl; + struct rt_lwp *owner_a; + struct rt_lwp *owner_b; + + owner_a = bind_owner(); + fd_a = create_lock_file(); + uassert_int_equal(set_lock(fd_a, F_SETLK, F_WRLCK, SEEK_SET, 0, 8), 0); + + owner_b = lwp_create(LWP_CREATE_FLAG_ALLOC_PID); + uassert_not_null(owner_b); + rt_thread_self()->lwp = owner_b; + fd_b = open(LOCK_PATH, O_RDWR, 0); + uassert_true(fd_b >= 0); + uassert_int_equal(set_lock(fd_b, F_SETLK, F_RDLCK, SEEK_SET, 0, 8), -1); + + rt_thread_self()->lwp = owner_a; + uassert_int_equal(close(fd_a), 0); + + rt_thread_self()->lwp = owner_b; + uassert_int_equal(set_lock(fd_b, F_SETLK, F_WRLCK, SEEK_SET, 0, 8), 0); + + rt_thread_self()->lwp = owner_a; + fd_a = open(LOCK_PATH, O_RDWR, 0); + uassert_true(fd_a >= 0); + uassert_int_equal(set_lock(fd_a, F_SETLK, F_WRLCK, SEEK_SET, 0, 8), -1); + + rt_thread_self()->lwp = owner_b; + fd_replace = open(LOCK_PATH, O_RDWR, 0); + uassert_true(fd_replace >= 0); + uassert_int_equal(sys_dup2(fd_replace, fd_b), fd_b); + + rt_thread_self()->lwp = owner_a; + uassert_int_equal(set_lock(fd_a, F_SETLK, F_WRLCK, SEEK_SET, 0, 8), 0); + uassert_int_equal(get_lock(fd_a, &fl, F_WRLCK, SEEK_SET, 0, 8), 0); + uassert_int_equal(fl.l_type, F_UNLCK); + + uassert_int_equal(close(fd_a), 0); + rt_thread_self()->lwp = owner_b; + uassert_int_equal(close(fd_b), 0); + uassert_int_equal(close(fd_replace), 0); + + unbind_owner(owner_a); + unbind_owner(owner_b); + uassert_int_equal(unlink(LOCK_PATH), 0); +#else + int fd = create_lock_file(); + + uassert_int_equal(set_lock(fd, F_SETLK, F_WRLCK, SEEK_SET, 0, 8), 0); + uassert_int_equal(close(fd), 0); + fd = open(LOCK_PATH, O_RDWR, 0); + uassert_true(fd >= 0); + uassert_int_equal(set_lock(fd, F_SETLK, F_WRLCK, SEEK_SET, 0, 8), 0); + uassert_int_equal(close(fd), 0); + uassert_int_equal(unlink(LOCK_PATH), 0); +#endif +} + +static rt_err_t utest_tc_init(void) +{ + unlink(LOCK_PATH); + return RT_EOK; +} + +static rt_err_t utest_tc_cleanup(void) +{ + unlink(LOCK_PATH); + return RT_EOK; +} + +static void testcase(void) +{ + UTEST_UNIT_RUN(test_positive_negative_len); + UTEST_UNIT_RUN(test_conflict_and_getlk); + UTEST_UNIT_RUN(test_interrupted_setlkw); + UTEST_UNIT_RUN(test_split_merge); + UTEST_UNIT_RUN(test_close_and_dup_release); +} +UTEST_TC_EXPORT(testcase, "components.dfs.record_lock", utest_tc_init, utest_tc_cleanup, 30); From 154bd62f6f8bae342c91c31d55708680fed01823 Mon Sep 17 00:00:00 2001 From: bernard Date: Sun, 23 Aug 2026 12:23:24 +0800 Subject: [PATCH 9/9] [style] Align clang-format for procfs and tmpfs Fix clang-format 22 alignment in loadavg macros, tmpfs setattr mask wrap, and record-lock utest defines so the format CI passes. --- components/dfs/dfs_v2/filesystems/procfs/proc_loadavg.c | 8 ++++---- components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c | 2 +- .../dfs/dfs_v2/filesystems/tmpfs/utest/tc_record_lock.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/components/dfs/dfs_v2/filesystems/procfs/proc_loadavg.c b/components/dfs/dfs_v2/filesystems/procfs/proc_loadavg.c index 44e95ff5f12b..d38d9a9c9280 100644 --- a/components/dfs/dfs_v2/filesystems/procfs/proc_loadavg.c +++ b/components/dfs/dfs_v2/filesystems/procfs/proc_loadavg.c @@ -31,11 +31,11 @@ struct loadavg_count }; /* Fixed-point load averages, 11 bits of fraction like Linux FSHIFT. */ -#define PROC_LOAD_FSHIFT 11 +#define PROC_LOAD_FSHIFT 11 #define PROC_LOAD_FIXED_1 (1UL << PROC_LOAD_FSHIFT) -#define PROC_LOAD_EXP_1 1884UL /* 1 / exp(5s / 60s) * 2048 */ -#define PROC_LOAD_EXP_5 2014UL /* 1 / exp(5s / 300s) * 2048 */ -#define PROC_LOAD_EXP_15 2037UL /* 1 / exp(5s / 900s) * 2048 */ +#define PROC_LOAD_EXP_1 1884UL /* 1 / exp(5s / 60s) * 2048 */ +#define PROC_LOAD_EXP_5 2014UL /* 1 / exp(5s / 300s) * 2048 */ +#define PROC_LOAD_EXP_15 2037UL /* 1 / exp(5s / 900s) * 2048 */ static unsigned long load_1 = 0; static unsigned long load_5 = 0; diff --git a/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c b/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c index 96c77c10041d..5f9299c11e43 100644 --- a/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c +++ b/components/dfs/dfs_v2/filesystems/tmpfs/dfs_tmpfs.c @@ -601,7 +601,7 @@ static int dfs_tmpfs_setattr(struct dfs_dentry *dentry, struct dfs_attr *attr) if (attr->ia_valid & ATTR_MODE_SET) { mode_t permissions = attr->st_mode & - (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX); + (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX); rt_spin_lock(&superblock->lock); d_file->mode = (d_file->mode & S_IFMT) | permissions; diff --git a/components/dfs/dfs_v2/filesystems/tmpfs/utest/tc_record_lock.c b/components/dfs/dfs_v2/filesystems/tmpfs/utest/tc_record_lock.c index ceb7214f697a..59ebc1badc2a 100644 --- a/components/dfs/dfs_v2/filesystems/tmpfs/utest/tc_record_lock.c +++ b/components/dfs/dfs_v2/filesystems/tmpfs/utest/tc_record_lock.c @@ -26,7 +26,7 @@ rt_err_t sys_dup2(int oldfd, int newfd); #include "utest.h" #include "utest_assert.h" -#define LOCK_PATH "/tmp/record-lock-utest" +#define LOCK_PATH "/tmp/record-lock-utest" #define LOCK_PAYLOAD "0123456789abcdef" struct lock_waiter_arg