123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658 |
- /*
- * Copyright (c) 2006-2023, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date Author Notes
- * 2023-12-02 Shell init ver.
- */
- #define DBG_TAG "filesystem.ptyfs"
- #define DBG_LVL DBG_INFO
- #include <rtdbg.h>
- #include "ptyfs.h"
- #include <dfs.h>
- #include <dfs_fs.h>
- #include <dfs_dentry.h>
- #include <dfs_file.h>
- #include <dfs_mnt.h>
- #include <devfs.h>
- #include <rid_bitmap.h>
- #include <rthw.h>
- #include <rtthread.h>
- #include <terminal/terminal.h>
- #include <dirent.h>
- #include <unistd.h>
- #ifndef S_IRWXUGO
- #define S_IRWXUGO (S_IRWXU | S_IRWXG | S_IRWXO)
- #endif /* S_IRWXUGO */
- #ifndef S_IALLUGO
- #define S_IALLUGO (S_ISUID | S_ISGID | S_ISVTX | S_IRWXUGO)
- #endif /* S_IALLUGO */
- #ifndef S_IRUGO
- #define S_IRUGO (S_IRUSR | S_IRGRP | S_IROTH)
- #endif /* S_IRUGO */
- #ifndef S_IWUGO
- #define S_IWUGO (S_IWUSR | S_IWGRP | S_IWOTH)
- #endif /* S_IWUGO */
- #ifndef S_IXUGO
- #define S_IXUGO (S_IXUSR | S_IXGRP | S_IXOTH)
- #endif /* S_IXUGO */
- #define PTYFS_MAGIC 0x9D94A07D
- #define PTYFS_TYPE_DIR 0x00
- #define PTYFS_TYPE_FILE_PTMX 0x01
- #define PTYFS_TYPE_FILE_SLAVE 0x02
- /* TODO: using Symbolic permission, but not ours */
- #define PTMX_DEFAULT_FILE_MODE (S_IFCHR | 0666)
- #define PTS_DEFAULT_FILE_MODE (S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP)
- #define ROOT_DEFUALT_FILE_MODE (S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR)
- struct ptyfs_sb;
- struct ptyfs_file
- {
- char basename[DIRENT_NAME_MAX]; /* file name */
- rt_uint32_t mode; /* file modes allowed */
- rt_uint32_t type; /* file type */
- rt_list_t subdirs; /* file subdir list */
- rt_list_t ent_node; /* entry node in subdir list */
- struct ptyfs_sb *sb; /* superblock ptr */
- rt_device_t device; /* device binding on this file */
- };
- struct ptyfs_sb
- {
- struct rt_device ptmx_device; /* ptmx device */
- struct rt_mutex lock; /* tmpfs lock */
- struct ptyfs_file root_file; /* root dir */
- struct ptyfs_file ptmx_file; /* `/ptmx` file */
- struct rid_bitmap ptsno_pool; /* pts number pool */
- rt_uint32_t magic; /* PTYFS_MAGIC */
- rt_size_t df_size; /* df size */
- rt_list_t sibling; /* sb sibling list */
- struct dfs_mnt *mount; /* mount data */
- /**
- * Note: This upper limit is set to protect kernel memory from draining
- * out by the application if it keeps allocating pty devices.
- *
- * Still, current implementation of bitmap can not efficiently use the
- * memory
- */
- rt_bitmap_t
- ptsno_pool_bitset[LWP_PTY_MAX_PARIS_LIMIT / (sizeof(rt_bitmap_t) * 8)];
- };
- static struct dfs_file_ops _default_fops;
- static int _split_out_subdir(const char *path, char *name)
- {
- const char *subpath = path;
- while (*subpath == '/' && *subpath)
- {
- subpath++;
- }
- while (*subpath != '/' && *subpath)
- {
- *name++ = *subpath++;
- }
- *name = '\0';
- return 0;
- }
- static rt_err_t ptyfile_init(struct ptyfs_file *file, struct ptyfs_sb *sb,
- const char *name, rt_uint32_t type,
- rt_uint32_t mode, rt_device_t device)
- {
- if (name)
- strncpy(file->basename, name, sizeof(file->basename));
- file->type = type;
- file->mode = mode;
- rt_list_init(&file->subdirs);
- rt_list_init(&file->ent_node);
- file->sb = sb;
- file->device = device;
- return 0;
- }
- static rt_err_t ptyfile_add_to_root(struct ptyfs_sb *sb,
- struct ptyfs_file *new_file)
- {
- struct ptyfs_file *root_file = &sb->root_file;
- /* update super block */
- sb->df_size += sizeof(struct ptyfs_file);
- rt_mutex_take(&sb->lock, RT_WAITING_FOREVER);
- rt_list_insert_after(&(root_file->subdirs), &(new_file->ent_node));
- rt_mutex_release(&sb->lock);
- return 0;
- }
- static rt_err_t ptyfile_remove_from_root(struct ptyfs_sb *sb,
- struct ptyfs_file *rm_file)
- {
- /* update super block */
- sb->df_size -= sizeof(struct ptyfs_file);
- rt_mutex_take(&sb->lock, RT_WAITING_FOREVER);
- rt_list_remove(&(rm_file->ent_node));
- rt_mutex_release(&sb->lock);
- return 0;
- }
- static struct ptyfs_file *ptyfile_lookup(struct ptyfs_sb *superblock,
- const char *path)
- {
- const char *subpath_iter, *curpath_iter, *basename = RT_NULL;
- char subdir_name[DIRENT_NAME_MAX];
- struct ptyfs_file *curfile, *found_file = RT_NULL;
- rt_list_t *list;
- int do_path_resolve = 1;
- subpath_iter = path;
- /* skip starting "/" */
- while (*subpath_iter == '/') subpath_iter++;
- if (!*subpath_iter)
- {
- return &(superblock->root_file);
- }
- curpath_iter = subpath_iter;
- curfile = &superblock->root_file;
- /* resolve chain of files splited from path one by one */
- while (do_path_resolve)
- {
- do_path_resolve = 0;
- /* splitout sub-directory or basename */
- while (*subpath_iter != '/' && *subpath_iter) subpath_iter++;
- if (!*subpath_iter)
- {
- basename = curpath_iter;
- }
- else
- {
- _split_out_subdir(curpath_iter, subdir_name);
- /* skip "/" for next search */
- subpath_iter++;
- }
- rt_mutex_take(&superblock->lock, RT_WAITING_FOREVER);
- rt_list_for_each(list, &curfile->subdirs)
- {
- struct ptyfs_file *file_iter;
- file_iter = rt_list_entry(list, struct ptyfs_file, ent_node);
- if (basename)
- {
- if (strcmp(file_iter->basename, basename) == 0)
- {
- found_file = file_iter;
- break;
- }
- }
- else if (strcmp(file_iter->basename, subdir_name) == 0)
- {
- curpath_iter = subpath_iter;
- curfile = file_iter;
- do_path_resolve = 1;
- break;
- }
- }
- rt_mutex_release(&superblock->lock);
- }
- return found_file;
- }
- const char *ptyfs_get_rootpath(rt_device_t ptmx)
- {
- const char *rc;
- struct ptyfs_sb *sb;
- /* allocate id for it and register file */
- sb = rt_container_of(ptmx, struct ptyfs_sb, ptmx_device);
- if (sb->magic != PTYFS_MAGIC)
- {
- rc = 0;
- }
- else
- {
- /* fullpath is always started with /dev/ */
- return sb->mount->fullpath + 5;
- }
- return rc;
- }
- ptsno_t ptyfs_register_pts(rt_device_t ptmx, rt_device_t pts)
- {
- ptsno_t rc;
- struct ptyfs_sb *sb;
- struct ptyfs_file *pts_file;
- struct rid_bitmap *ptsno_pool;
- /* allocate id for it and register file */
- sb = rt_container_of(ptmx, struct ptyfs_sb, ptmx_device);
- if (sb->magic != PTYFS_MAGIC)
- {
- rc = -1;
- }
- else
- {
- ptsno_pool = &sb->ptsno_pool;
- rc = rid_bitmap_get(ptsno_pool);
- if (rc >= 0)
- {
- pts_file = rt_calloc(1, sizeof(struct ptyfs_file));
- if (pts_file)
- {
- snprintf(pts_file->basename, DIRENT_NAME_MAX, "%lu", (unsigned long)rc);
- ptyfile_init(pts_file, sb, 0, PTYFS_TYPE_FILE_SLAVE,
- PTS_DEFAULT_FILE_MODE, pts);
- ptyfile_add_to_root(sb, pts_file);
- }
- else
- {
- rid_bitmap_put(ptsno_pool, rc);
- rc = -1;
- }
- }
- /* else rc == -1 */
- }
- return rc;
- }
- rt_err_t ptyfs_unregister_pts(rt_device_t ptmx, ptsno_t ptsno)
- {
- ptsno_t rc;
- struct ptyfs_sb *sb;
- struct ptyfs_file *pts_file;
- struct rid_bitmap *ptsno_pool;
- char path_buf[DIRENT_NAME_MAX];
- /* allocate id for it and register file */
- sb = rt_container_of(ptmx, struct ptyfs_sb, ptmx_device);
- if (sb->magic != PTYFS_MAGIC || ptsno < 0)
- {
- rc = -EINVAL;
- }
- else
- {
- /* get path and findout device */
- snprintf(path_buf, sizeof(path_buf), "%lu", (unsigned long)ptsno);
- pts_file = ptyfile_lookup(sb, path_buf);
- if (pts_file)
- {
- ptyfile_remove_from_root(sb, pts_file);
- ptsno_pool = &sb->ptsno_pool;
- rid_bitmap_put(ptsno_pool, ptsno);
- rc = 0;
- }
- else
- {
- rc = -ENOENT;
- }
- }
- return rc;
- }
- #define DEVFS_PREFIX "/dev/"
- #define DEVFS_PREFIX_LEN (sizeof(DEVFS_PREFIX) - 1)
- /**
- * Create an new instance of ptyfs, and mount on target point
- * 2 basic files are created: root, ptmx.
- *
- * todo: support of mount options?
- */
- static int ptyfs_ops_mount(struct dfs_mnt *mnt, unsigned long rwflag,
- const void *data)
- {
- struct ptyfs_sb *sb;
- rt_device_t ptmx_device;
- rt_err_t rc;
- if (strncmp(mnt->fullpath, DEVFS_PREFIX, DEVFS_PREFIX_LEN) != 0)
- {
- LOG_I("%s() Not mounted on `/dev/'", __func__);
- return -EINVAL;
- }
- sb = rt_calloc(1, sizeof(struct ptyfs_sb));
- if (sb)
- {
- rt_mutex_init(&sb->lock, "ptyfs", RT_IPC_FLAG_PRIO);
- /* setup the ptmx device */
- ptmx_device = &sb->ptmx_device;
- rc = lwp_ptmx_init(ptmx_device, mnt->fullpath + DEVFS_PREFIX_LEN);
- if (rc == RT_EOK)
- {
- /* setup 2 basic files */
- ptyfile_init(&sb->root_file, sb, "/", PTYFS_TYPE_DIR,
- ROOT_DEFUALT_FILE_MODE, 0);
- ptyfile_init(&sb->ptmx_file, sb, "ptmx", PTYFS_TYPE_FILE_PTMX,
- PTMX_DEFAULT_FILE_MODE, ptmx_device);
- ptyfile_add_to_root(sb, &sb->ptmx_file);
- /* setup rid */
- rid_bitmap_init(&sb->ptsno_pool, 0, LWP_PTY_MAX_PARIS_LIMIT,
- sb->ptsno_pool_bitset, &sb->lock);
- /* setup properties and members */
- sb->magic = PTYFS_MAGIC;
- sb->df_size = sizeof(struct ptyfs_sb);
- rt_list_init(&sb->sibling);
- /* binding superblocks and mount point */
- mnt->data = sb;
- sb->mount = mnt;
- rc = 0;
- }
- /* else just return rc */
- }
- else
- {
- rc = -ENOMEM;
- }
- return rc;
- }
- static int ptyfs_ops_umount(struct dfs_mnt *mnt)
- {
- /* Not supported yet */
- return -1;
- }
- static int ptyfs_ops_setattr(struct dfs_dentry *dentry, struct dfs_attr *attr)
- {
- struct ptyfs_file *pty_file;
- struct ptyfs_sb *superblock;
- RT_ASSERT(dentry);
- RT_ASSERT(dentry->mnt);
- superblock = (struct ptyfs_sb *)dentry->mnt->data;
- RT_ASSERT(superblock);
- /* find the device related to current pts slave device */
- pty_file = ptyfile_lookup(superblock, dentry->pathname);
- if (pty_file && pty_file->type == PTYFS_TYPE_FILE_SLAVE)
- {
- pty_file->mode &= ~0xFFF;
- pty_file->mode |= attr->st_mode & 0xFFF;
- return 0;
- }
- return -1;
- }
- #define OPTIMAL_BSIZE 1024
- static int ptyfs_ops_statfs(struct dfs_mnt *mnt, struct statfs *buf)
- {
- struct ptyfs_sb *superblock;
- RT_ASSERT(mnt != NULL);
- RT_ASSERT(buf != NULL);
- superblock = (struct ptyfs_sb *)mnt->data;
- RT_ASSERT(superblock != NULL);
- buf->f_bsize = OPTIMAL_BSIZE;
- buf->f_blocks = (superblock->df_size + OPTIMAL_BSIZE - 1) / OPTIMAL_BSIZE;
- buf->f_bfree = 1;
- buf->f_bavail = buf->f_bfree;
- return RT_EOK;
- }
- static int ptyfs_ops_stat(struct dfs_dentry *dentry, struct stat *st)
- {
- struct dfs_vnode *vnode;
- if (dentry && dentry->vnode)
- {
- vnode = dentry->vnode;
- /* device id ? */
- st->st_dev = (dev_t)(long)(dentry->mnt->dev_id);
- st->st_ino = (ino_t)dfs_dentry_full_path_crc32(dentry);
- st->st_gid = vnode->gid;
- st->st_uid = vnode->uid;
- st->st_mode = vnode->mode;
- st->st_nlink = vnode->nlink;
- st->st_size = vnode->size;
- st->st_mtim.tv_nsec = vnode->mtime.tv_nsec;
- st->st_mtim.tv_sec = vnode->mtime.tv_sec;
- st->st_ctim.tv_nsec = vnode->ctime.tv_nsec;
- st->st_ctim.tv_sec = vnode->ctime.tv_sec;
- st->st_atim.tv_nsec = vnode->atime.tv_nsec;
- st->st_atim.tv_sec = vnode->atime.tv_sec;
- }
- return 0;
- }
- static struct dfs_vnode *ptyfs_ops_lookup(struct dfs_dentry *dentry)
- {
- struct dfs_vnode *vnode = RT_NULL;
- struct ptyfs_sb *superblock;
- struct ptyfs_file *pty_file;
- if (dentry == NULL || dentry->mnt == NULL || dentry->mnt->data == NULL)
- {
- return NULL;
- }
- superblock = (struct ptyfs_sb *)dentry->mnt->data;
- pty_file = ptyfile_lookup(superblock, dentry->pathname);
- if (pty_file)
- {
- vnode = dfs_vnode_create();
- if (vnode)
- {
- vnode->data = pty_file->device;
- vnode->nlink = 1;
- vnode->size = 0;
- vnode->mnt = dentry->mnt;
- /* if it's root directory */
- vnode->fops = &_default_fops;
- vnode->mode = pty_file->mode;
- vnode->type = pty_file->type == PTYFS_TYPE_DIR ? FT_DIRECTORY : FT_DEVICE;
- }
- }
- return vnode;
- }
- static struct dfs_vnode *ptyfs_ops_create_vnode(struct dfs_dentry *dentry,
- int type, mode_t mode)
- {
- struct dfs_vnode *vnode = RT_NULL;
- struct ptyfs_sb *sb;
- struct ptyfs_file *pty_file;
- char *vnode_path;
- if (dentry == NULL || dentry->mnt == NULL || dentry->mnt->data == NULL)
- {
- return NULL;
- }
- sb = (struct ptyfs_sb *)dentry->mnt->data;
- RT_ASSERT(sb != NULL);
- vnode = dfs_vnode_create();
- if (vnode)
- {
- vnode_path = dentry->pathname;
- /* Query if file existed. Filter out illegal open modes */
- pty_file = ptyfile_lookup(sb, vnode_path);
- if (!pty_file || (~pty_file->mode & mode))
- {
- dfs_vnode_destroy(vnode);
- return NULL;
- }
- vnode->data = pty_file->device;
- vnode->nlink = 1;
- vnode->size = 0;
- vnode->mnt = dentry->mnt;
- vnode->fops = pty_file->device ? pty_file->device->fops : RT_NULL;
- vnode->mode &= pty_file->mode;
- if (type == FT_DIRECTORY)
- {
- vnode->mode |= S_IFDIR;
- vnode->type = FT_DIRECTORY;
- LOG_I("%s: S_IFDIR created", __func__);
- }
- else if (type == FT_REGULAR)
- {
- vnode->mode |= S_IFCHR;
- vnode->type = FT_DEVICE;
- LOG_I("%s: S_IFDIR created", __func__);
- }
- else
- {
- /* unsupported types */
- dfs_vnode_destroy(vnode);
- return NULL;
- }
- }
- return vnode;
- }
- static int ptyfs_ops_free_vnode(struct dfs_vnode *vnode)
- {
- return RT_EOK;
- }
- static int devpty_deffops_getdents(struct dfs_file *file, struct dirent *dirp,
- uint32_t count)
- {
- struct ptyfs_file *d_file;
- struct ptyfs_sb *superblock;
- RT_ASSERT(file);
- RT_ASSERT(file->dentry);
- RT_ASSERT(file->dentry->mnt);
- superblock = (struct ptyfs_sb *)file->dentry->mnt->data;
- RT_ASSERT(superblock);
- d_file = ptyfile_lookup(superblock, file->dentry->pathname);
- if (d_file)
- {
- rt_size_t index, end;
- struct dirent *d;
- struct ptyfs_file *n_file;
- rt_list_t *list;
- /* make integer count */
- count = (count / sizeof(struct dirent));
- if (count == 0)
- {
- return -EINVAL;
- }
- end = file->fpos + count;
- index = 0;
- count = 0;
- rt_list_for_each(list, &d_file->subdirs)
- {
- if (index >= (rt_size_t)file->fpos)
- {
- n_file = rt_list_entry(list, struct ptyfs_file, ent_node);
- d = dirp + count;
- if (n_file->type == PTYFS_TYPE_DIR)
- {
- d->d_type = DT_DIR;
- }
- else
- {
- /* ptmx(5,2) or slave(136,[0,1048575]) device, on Linux */
- d->d_type = DT_CHR;
- }
- d->d_reclen = (rt_uint16_t)sizeof(struct dirent);
- rt_strncpy(d->d_name, n_file->basename, DIRENT_NAME_MAX);
- d->d_namlen = rt_strlen(d->d_name);
- count += 1;
- file->fpos += 1;
- }
- index += 1;
- if (index >= end)
- {
- break;
- }
- }
- }
- return count * sizeof(struct dirent);
- }
- static const struct dfs_filesystem_ops _ptyfs_ops = {
- .name = "ptyfs",
- .flags = DFS_FS_FLAG_DEFAULT,
- .default_fops = &_default_fops,
- .mount = ptyfs_ops_mount,
- .umount = ptyfs_ops_umount,
- /* don't allow to create symbolic link */
- .symlink = RT_NULL,
- .readlink = RT_NULL,
- .unlink = RT_NULL,
- .setattr = ptyfs_ops_setattr,
- .statfs = ptyfs_ops_statfs,
- .stat = ptyfs_ops_stat,
- .lookup = ptyfs_ops_lookup,
- .create_vnode = ptyfs_ops_create_vnode,
- .free_vnode = ptyfs_ops_free_vnode,
- };
- static struct dfs_filesystem_type _devptyfs = {
- .fs_ops = &_ptyfs_ops,
- };
- static int _ptyfs_init(void)
- {
- _default_fops = *dfs_devfs_fops();
- _default_fops.getdents = devpty_deffops_getdents;
- /* register file system */
- dfs_register(&_devptyfs);
- return 0;
- }
- INIT_COMPONENT_EXPORT(_ptyfs_init);
|