mirror of
https://github.com/RT-Thread/rt-thread.git
synced 2026-08-20 21:29:42 +08:00
Add AF_UNIX sockets implementation (#11681)
* components/dfs: support unix socket nodes AF_UNIX pathname binding and descriptor passing require DFSv2 socket nodes and retained open file descriptions across fd tables. Add socket-node creation for tmpfs and devtmpfs, fd reference helpers, and socket F_SETFL forwarding. Impact: DFSv2 socket nodes and descriptor reference handling. Validation: git diff --cached --check. * components/net/sal: support local protocol providers Local IPC protocol families do not have a backing network device. Store the selected provider in each SAL socket and add a local provider registry while preserving netdev checks for Internet sockets. Handle DFSv2 close semantics, socketpair flags, and MSG_CTRUNC for AF_UNIX integration. Impact: SAL protocol dispatch for all socket families. Validation: git diff --cached --check. * components/lwp: support unix socket messages Musl AF_UNIX addresses and ancillary data require explicit ABI and user-memory conversion at the LWP syscall boundary. Add the musl msghdr layout, bounded address and message copying, control-message level conversion, and MSG_CTRUNC translation. Correct receive buffer allocation and copy lengths while handling messages. Impact: LWP socket syscalls when SAL is enabled. Validation: git diff --cached --check. * components/net/af_unix: add local sockets Add an opt-in AF_UNIX provider for pathname-based local IPC without a synthetic network device. Support datagram and stream sockets, blocking and nonblocking I/O, timeouts, poll, socketpair, and SCM_RIGHTS descriptor passing. Include bounded Kconfig settings, component documentation, and utest coverage. Impact: enabled only by RT_USING_AF_UNIX and requires SAL POSIX with DFSv2. Validation: git diff --cached --check. * Fix AF_UNIX CI checks Apply the repository clang-format rules to the affected source lines. Suppress the cppcheck false positive for the devtmpfs list iterator. No functional behavior is changed. * components/lwp: preserve NULL optional msghdr buffers When msg_name or msg_control is NULL, keep the kernel pointer NULL instead of substituting an uninitialized buffer. Reject a NULL control buffer with a nonzero length as EFAULT. Impact: LWP sendmsg/recvmsg conversion on MMU targets. Validation: git diff --cached --check. * components/net/af_unix: fix poll UAF, namespace leak, and SCM_RIGHTS cycles Poll only the local wait queue and wake writers from the receiver. Free namespace entries on detach, and reject AF_UNIX descriptors in SCM_RIGHTS until cycle collection exists. Impact: AF_UNIX poll, bind lifetime, and descriptor passing. Validation: git diff --cached --check. * components/dfs: add DFSv2 AF_UNIX utest suite Add opt-in tests under dfs/utest/v2/af_unix for socket nodes, IPC, poll peer-close, namespace cleanup, and SCM_RIGHTS rejection. Sources are built only when RT_UTEST_TC_USING_DFS_V2_AF_UNIX and the selected test groups are enabled. Impact: DFS utest menu and build; no production AF_UNIX behavior change. Validation: git diff --cached --check.
This commit is contained in:
@@ -27,6 +27,7 @@
|
||||
#define TMPFS_TYPE_FILE 0x00
|
||||
#define TMPFS_TYPE_DIR 0x01
|
||||
#define TMPFS_TYPE_DYN_DEV 0x02 /* dynamic device */
|
||||
#define TMPFS_TYPE_SOCKET 0x03
|
||||
|
||||
struct devtmpfs_sb;
|
||||
|
||||
@@ -223,6 +224,7 @@ find_subpath:
|
||||
if (rt_strcmp(file->name, filename) == 0)
|
||||
{
|
||||
rt_spin_unlock(&superblock->lock);
|
||||
/* cppcheck-suppress uninitvar */
|
||||
return file;
|
||||
}
|
||||
}
|
||||
@@ -325,6 +327,10 @@ static int devtmpfs_getdents(struct dfs_file *file, struct dirent *dirp, uint32_
|
||||
{
|
||||
d->d_type = DT_DIR;
|
||||
}
|
||||
if (n_file->type == TMPFS_TYPE_SOCKET)
|
||||
{
|
||||
d->d_type = DT_SOCK;
|
||||
}
|
||||
|
||||
d->d_reclen = (rt_uint16_t)sizeof(struct dirent);
|
||||
rt_strncpy(d->d_name, n_file->name, DIRENT_NAME_MAX);
|
||||
@@ -540,6 +546,14 @@ static struct dfs_vnode *devtmpfs_create_vnode(struct dfs_dentry *dentry, int ty
|
||||
vnode->mode &= ~S_IFMT;
|
||||
vnode->mode |= S_IFDIR;
|
||||
}
|
||||
else if (type == FT_SOCKET ||
|
||||
(type == FT_REGULAR && S_ISSOCK(mode)))
|
||||
{
|
||||
d_file->type = TMPFS_TYPE_SOCKET;
|
||||
vnode->type = FT_SOCKET;
|
||||
vnode->mode &= ~S_IFMT;
|
||||
vnode->mode |= S_IFSOCK;
|
||||
}
|
||||
else
|
||||
{
|
||||
d_file->type = TMPFS_TYPE_FILE;
|
||||
@@ -585,6 +599,10 @@ static struct dfs_vnode *devtmpfs_lookup(struct dfs_dentry *dentry)
|
||||
{
|
||||
vnode->type = FT_DIRECTORY;
|
||||
}
|
||||
else if (d_file->type == TMPFS_TYPE_SOCKET)
|
||||
{
|
||||
vnode->type = FT_SOCKET;
|
||||
}
|
||||
else if (d_file->link)
|
||||
{
|
||||
vnode->type = FT_SYMLINK;
|
||||
|
||||
@@ -535,6 +535,10 @@ static int dfs_tmpfs_getdents(struct dfs_file *file,
|
||||
{
|
||||
d->d_type = DT_DIR;
|
||||
}
|
||||
if (n_file->type == TMPFS_TYPE_SOCKET)
|
||||
{
|
||||
d->d_type = DT_SOCK;
|
||||
}
|
||||
d->d_namlen = RT_NAME_MAX;
|
||||
d->d_reclen = (rt_uint16_t)sizeof(struct dirent);
|
||||
rt_strncpy(d->d_name, n_file->name, TMPFS_NAME_MAX);
|
||||
@@ -664,6 +668,11 @@ static struct dfs_vnode *_dfs_tmpfs_lookup(struct dfs_dentry *dentry)
|
||||
vnode->mode = S_IFDIR | (S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
|
||||
vnode->type = FT_DIRECTORY;
|
||||
}
|
||||
else if (d_file->type == TMPFS_TYPE_SOCKET)
|
||||
{
|
||||
vnode->mode = S_IFSOCK | (S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
vnode->type = FT_SOCKET;
|
||||
}
|
||||
else
|
||||
{
|
||||
vnode->mode = S_IFREG | (S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
@@ -750,6 +759,13 @@ static struct dfs_vnode *dfs_tmpfs_create_vnode(struct dfs_dentry *dentry, int t
|
||||
vnode->mode = S_IFDIR | (S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
|
||||
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));
|
||||
vnode->type = FT_SOCKET;
|
||||
}
|
||||
else
|
||||
{
|
||||
d_file->type = TMPFS_TYPE_FILE;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#define TMPFS_TYPE_FILE 0x00
|
||||
#define TMPFS_TYPE_DIR 0x01
|
||||
#define TMPFS_TYPE_SOCKET 0x02
|
||||
|
||||
struct tmpfs_sb;
|
||||
|
||||
@@ -46,4 +47,3 @@ struct tmpfs_sb
|
||||
int dfs_tmpfs_init(void);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -134,6 +134,12 @@ int fdt_fd_associate_file(struct dfs_fdtable *fdt, int fd, struct dfs_file *file
|
||||
struct dfs_file *fd_get(int fd);
|
||||
void fd_release(int fd);
|
||||
|
||||
/* Reference helpers used when an open file description crosses fd tables. */
|
||||
int dfs_file_get_refs(const int *fds, size_t count, struct dfs_file **files);
|
||||
/* Successful installation transfers the supplied references to the fd table. */
|
||||
int dfs_file_install_refs(struct dfs_file **files, size_t count, int *fds);
|
||||
void dfs_file_put_ref(struct dfs_file *file);
|
||||
|
||||
void fd_init(struct dfs_file *fd);
|
||||
|
||||
struct dfs_fdtable *dfs_fdtable_get(void);
|
||||
|
||||
@@ -149,6 +149,7 @@ void dfs_file_init(struct dfs_file *file);
|
||||
void dfs_file_deinit(struct dfs_file *file);
|
||||
|
||||
int dfs_file_open(struct dfs_file *file, const char *path, int flags, mode_t mode);
|
||||
int dfs_file_mknod(const char *path, int type, mode_t mode);
|
||||
int dfs_file_close(struct dfs_file *file);
|
||||
|
||||
off_t dfs_file_get_fpos(struct dfs_file *file);
|
||||
|
||||
@@ -349,23 +349,29 @@ int fdt_fd_new(struct dfs_fdtable *fdt)
|
||||
*/
|
||||
void fdt_fd_release(struct dfs_fdtable *fdt, int fd)
|
||||
{
|
||||
if (fd < fdt->maxfd)
|
||||
if (fdt == RT_NULL || dfs_file_lock() != RT_EOK)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (fd >= 0 && fd < (int)fdt->maxfd)
|
||||
{
|
||||
struct dfs_file *file;
|
||||
|
||||
file = fdt_get_file(fdt, fd);
|
||||
|
||||
if (file && file->ref_count == 1)
|
||||
if (file != RT_NULL && file->ref_count == 1)
|
||||
{
|
||||
dfs_file_destroy(file);
|
||||
}
|
||||
else
|
||||
else if (file != RT_NULL)
|
||||
{
|
||||
rt_atomic_sub(&(file->ref_count), 1);
|
||||
}
|
||||
|
||||
fdt->fds[fd] = RT_NULL;
|
||||
}
|
||||
dfs_file_unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -493,6 +499,106 @@ struct dfs_file *fd_get(int fd)
|
||||
return fdt_get_file(fdt, fd);
|
||||
}
|
||||
|
||||
int dfs_file_get_refs(const int *fds, size_t count, struct dfs_file **files)
|
||||
{
|
||||
size_t index;
|
||||
struct dfs_fdtable *fdt;
|
||||
|
||||
if ((count != 0 && (fds == RT_NULL || files == RT_NULL)) ||
|
||||
dfs_file_lock() != RT_EOK)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fdt = dfs_fdtable_get();
|
||||
for (index = 0; index < count; index++)
|
||||
{
|
||||
files[index] = fdt_get_file(fdt, fds[index]);
|
||||
if (files[index] == RT_NULL ||
|
||||
(files[index]->dentry == RT_NULL && files[index]->vnode == RT_NULL))
|
||||
{
|
||||
dfs_file_unlock();
|
||||
return -EBADF;
|
||||
}
|
||||
}
|
||||
|
||||
for (index = 0; index < count; index++)
|
||||
{
|
||||
rt_atomic_add(&files[index]->ref_count, 1);
|
||||
}
|
||||
dfs_file_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dfs_file_install_refs(struct dfs_file **files, size_t count, int *fds)
|
||||
{
|
||||
int fd;
|
||||
int startfd;
|
||||
size_t index;
|
||||
struct dfs_fdtable *fdt;
|
||||
|
||||
if ((count != 0 && (files == RT_NULL || fds == RT_NULL)) ||
|
||||
dfs_file_lock() != RT_EOK)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fdt = dfs_fdtable_get();
|
||||
startfd = (fdt == &_fdtab) ? DFS_STDIO_OFFSET : 0;
|
||||
for (index = 0; index < count; index++)
|
||||
{
|
||||
if (files[index] == RT_NULL || files[index]->magic != DFS_FD_MAGIC)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
fd = _fdt_slot_alloc(fdt, startfd);
|
||||
if (fd < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
fdt->fds[fd] = files[index];
|
||||
fds[index] = fd;
|
||||
}
|
||||
|
||||
if (index != count)
|
||||
{
|
||||
while (index > 0)
|
||||
{
|
||||
index--;
|
||||
fdt->fds[fds[index]] = RT_NULL;
|
||||
}
|
||||
dfs_file_unlock();
|
||||
return -EMFILE;
|
||||
}
|
||||
|
||||
dfs_file_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dfs_file_put_ref(struct dfs_file *file)
|
||||
{
|
||||
if (file == RT_NULL || dfs_file_lock() != RT_EOK)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (file->magic == DFS_FD_MAGIC &&
|
||||
rt_atomic_load(&file->ref_count) > 0 &&
|
||||
dfs_file_close(file) == 0)
|
||||
{
|
||||
if (rt_atomic_load(&file->ref_count) == 1)
|
||||
{
|
||||
dfs_file_destroy(file);
|
||||
}
|
||||
else
|
||||
{
|
||||
rt_atomic_sub(&file->ref_count, 1);
|
||||
}
|
||||
}
|
||||
dfs_file_unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function will get the file descriptor table of current process.
|
||||
*/
|
||||
@@ -1238,4 +1344,4 @@ MSH_CMD_EXPORT(dfs_dlog, dfs dlog on|off);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
/** @} */
|
||||
/** @} */
|
||||
|
||||
@@ -823,6 +823,128 @@ _ERR_RET:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int dfs_file_mknod(const char *path, int type, mode_t mode)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
int create_type = type;
|
||||
mode_t create_mode = mode;
|
||||
char *fullpath;
|
||||
struct dfs_mnt *mnt;
|
||||
struct dfs_dentry *dentry;
|
||||
|
||||
if (path == RT_NULL || type < FT_REGULAR || type > FT_NONLOCK)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fullpath = dfs_normalize_path(RT_NULL, path);
|
||||
if (fullpath == RT_NULL)
|
||||
{
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mnt = dfs_mnt_lookup(fullpath);
|
||||
if (mnt == RT_NULL)
|
||||
{
|
||||
ret = -ENOENT;
|
||||
goto __exit;
|
||||
}
|
||||
|
||||
{
|
||||
char *realpath;
|
||||
|
||||
realpath = dfs_file_realpath(&mnt, fullpath, DFS_REALPATH_EXCEPT_LAST);
|
||||
if (realpath != RT_NULL)
|
||||
{
|
||||
rt_free(fullpath);
|
||||
fullpath = realpath;
|
||||
}
|
||||
}
|
||||
|
||||
if (strcmp(mnt->fullpath, fullpath) == 0)
|
||||
{
|
||||
ret = -EEXIST;
|
||||
goto __exit;
|
||||
}
|
||||
|
||||
dentry = dfs_dentry_lookup(mnt, fullpath, 0);
|
||||
if (dentry != RT_NULL)
|
||||
{
|
||||
dfs_dentry_unref(dentry);
|
||||
ret = -EEXIST;
|
||||
goto __exit;
|
||||
}
|
||||
|
||||
if (mnt->fs_ops->create_vnode == RT_NULL)
|
||||
{
|
||||
ret = -ENOSYS;
|
||||
goto __exit;
|
||||
}
|
||||
|
||||
if (type == FT_SOCKET)
|
||||
{
|
||||
create_type = FT_REGULAR;
|
||||
create_mode = (mode & ~S_IFMT) | S_IFSOCK;
|
||||
}
|
||||
|
||||
ret = dfs_file_lock();
|
||||
if (ret != RT_EOK)
|
||||
{
|
||||
goto __exit;
|
||||
}
|
||||
|
||||
dentry = dfs_dentry_create(mnt, fullpath);
|
||||
if (dentry != RT_NULL)
|
||||
{
|
||||
struct dfs_vnode *vnode = RT_NULL;
|
||||
|
||||
if (dfs_is_mounted(mnt) == 0)
|
||||
{
|
||||
vnode = mnt->fs_ops->create_vnode(dentry, create_type,
|
||||
create_mode);
|
||||
}
|
||||
if (vnode != RT_NULL)
|
||||
{
|
||||
if (type == FT_SOCKET && !S_ISSOCK(vnode->mode))
|
||||
{
|
||||
if (mnt->fs_ops->unlink != RT_NULL)
|
||||
{
|
||||
dentry->vnode = vnode;
|
||||
(void)mnt->fs_ops->unlink(dentry);
|
||||
dentry->vnode = RT_NULL;
|
||||
}
|
||||
dfs_vnode_unref(vnode);
|
||||
ret = -EOPNOTSUPP;
|
||||
}
|
||||
else
|
||||
{
|
||||
vnode->type = type;
|
||||
dentry->vnode = vnode;
|
||||
dfs_dentry_insert(dentry);
|
||||
ret = RT_EOK;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = -ENOENT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = -ENOMEM;
|
||||
}
|
||||
dfs_file_unlock();
|
||||
|
||||
if (dentry != RT_NULL)
|
||||
{
|
||||
dfs_dentry_unref(dentry);
|
||||
}
|
||||
|
||||
__exit:
|
||||
rt_free(fullpath);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Close a file and release associated resources
|
||||
*
|
||||
@@ -1588,6 +1710,15 @@ int dfs_file_fcntl(int fd, int cmd, unsigned long arg)
|
||||
O_APPEND | O_NONBLOCK;
|
||||
|
||||
flags &= mask;
|
||||
if (file->vnode->type == FT_SOCKET && file->fops != RT_NULL &&
|
||||
file->fops->ioctl != RT_NULL)
|
||||
{
|
||||
ret = file->fops->ioctl(file, F_SETFL, (void *)(rt_base_t)flags);
|
||||
if (ret < 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
file->flags &= ~mask;
|
||||
file->flags |= flags;
|
||||
break;
|
||||
@@ -3035,4 +3166,4 @@ void copy(const char *src, const char *dst)
|
||||
}
|
||||
FINSH_FUNCTION_EXPORT(copy, copy file or dir)
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -40,5 +40,8 @@ if RT_USING_DFS
|
||||
This device will be formatted and mounted during testing
|
||||
|
||||
endif
|
||||
|
||||
rsource "v2/af_unix/Kconfig"
|
||||
|
||||
endmenu
|
||||
endif
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
Import('rtconfig')
|
||||
from building import *
|
||||
import os
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
src = []
|
||||
@@ -15,6 +16,7 @@ if GetDepend('RT_UTEST_TC_USING_DFS_API'):
|
||||
src += ['tc_posix_api.c']
|
||||
|
||||
# Define the test group with proper dependencies
|
||||
group = DefineGroup('utestcases', src, depend = ['RT_USING_UTESTCASES', 'RT_USING_DFS'], CPPPATH = CPPPATH)
|
||||
objs = DefineGroup('utestcases', src, depend = ['RT_USING_UTESTCASES', 'RT_USING_DFS'], CPPPATH = CPPPATH)
|
||||
objs = objs + SConscript(os.path.join('v2', 'af_unix', 'SConscript'))
|
||||
|
||||
Return('group')
|
||||
Return('objs')
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
menuconfig RT_UTEST_TC_USING_DFS_V2_AF_UNIX
|
||||
bool "DFSv2 AF_UNIX Test"
|
||||
default n
|
||||
depends on RT_USING_DFS_V2
|
||||
depends on RT_USING_AF_UNIX
|
||||
depends on SAL_USING_POSIX
|
||||
help
|
||||
Enable DFSv2 AF_UNIX unit tests. Sources are compiled only when
|
||||
this option and the selected test groups below are enabled.
|
||||
|
||||
Coverage includes socket-node creation, transferable file
|
||||
references, datagram and stream IPC, poll readiness, pathname
|
||||
lifetime, namespace cleanup, and SCM_RIGHTS handling.
|
||||
|
||||
if RT_UTEST_TC_USING_DFS_V2_AF_UNIX
|
||||
|
||||
config RT_UTEST_DFS_V2_AF_UNIX_DIR
|
||||
string "Directory for AF_UNIX pathname tests"
|
||||
default "/dev"
|
||||
help
|
||||
Directory used to create temporary AF_UNIX pathname nodes.
|
||||
The filesystem mounted here must support S_IFSOCK nodes
|
||||
through create_vnode(), such as tmpfs or devtmpfs.
|
||||
|
||||
config RT_UTEST_DFS_V2_AF_UNIX_NODE
|
||||
bool "Socket node and file-reference tests"
|
||||
default y
|
||||
help
|
||||
Build tests for DFSv2 socket nodes and transferable file
|
||||
references:
|
||||
* dfs_file_mknod(FT_SOCKET) creates an S_ISSOCK node
|
||||
* creating the same path again returns EEXIST
|
||||
* unlink() removes the socket node
|
||||
* dfs_file_get_refs() rejects an invalid fd
|
||||
* get_refs() + put_ref() keeps the original fd usable
|
||||
* get_refs() + install_refs() transfers a pipe and shares
|
||||
the open file description
|
||||
|
||||
config RT_UTEST_DFS_V2_AF_UNIX_IPC
|
||||
bool "Datagram, stream and socketpair IPC tests"
|
||||
default y
|
||||
help
|
||||
Build tests for AF_UNIX IPC operations:
|
||||
* SOCK_DGRAM bind, connect, send and recvfrom
|
||||
* SOCK_STREAM listen, accept, send, recv and shutdown
|
||||
* socketpair() for both SOCK_DGRAM and SOCK_STREAM
|
||||
* sendmsg()/recvmsg() scatter-gather I/O
|
||||
* connected sendmsg() with a NULL msg_name
|
||||
* sendmsg() with a NULL msg_control and nonzero length
|
||||
* listen() on SOCK_DGRAM returns EOPNOTSUPP
|
||||
* connect() to a missing pathname returns ENOENT
|
||||
* non-blocking recv() returns EAGAIN
|
||||
|
||||
config RT_UTEST_DFS_V2_AF_UNIX_POLL
|
||||
bool "Poll readiness and peer-close tests"
|
||||
default y
|
||||
help
|
||||
Build tests for AF_UNIX poll/select readiness:
|
||||
* empty stream socket reports POLLOUT and not POLLIN
|
||||
* received data reports POLLIN
|
||||
* filling the stream buffer clears POLLOUT; a peer recv
|
||||
makes POLLOUT ready again
|
||||
* blocking poll(POLLIN) wakes with POLLHUP/POLLIN when
|
||||
the peer is closed (covers the peer-queue UAF case)
|
||||
* datagram queue-full and POLLIN readiness
|
||||
|
||||
config RT_UTEST_DFS_V2_AF_UNIX_NAMESPACE
|
||||
bool "Pathname bind, unlink and namespace tests"
|
||||
default y
|
||||
help
|
||||
Build tests for pathname lifetime and namespace cleanup:
|
||||
* bind() creates a persistent S_IFSOCK node
|
||||
* close() leaves the pathname in the filesystem
|
||||
* rebinding without unlink() returns EADDRINUSE
|
||||
* unlink() then bind() succeeds
|
||||
* connect() after close without unlink() returns
|
||||
ECONNREFUSED
|
||||
* connect() after unlink() returns ENOENT
|
||||
* repeated bind/close/unlink on distinct paths does not
|
||||
leak namespace entries
|
||||
|
||||
config RT_UTEST_DFS_V2_AF_UNIX_RIGHTS
|
||||
bool "SCM_RIGHTS transfer and rejection tests"
|
||||
default y
|
||||
help
|
||||
Build tests for SCM_RIGHTS descriptor passing:
|
||||
* transfer pipe descriptors over SOCK_DGRAM and
|
||||
SOCK_STREAM
|
||||
* recvmsg() without a control buffer reports MSG_CTRUNC
|
||||
* MSG_PEEK does not install or consume descriptors
|
||||
* an invalid fd returns EBADF
|
||||
* passing an AF_UNIX socket descriptor returns
|
||||
EOPNOTSUPP and does not create a reference cycle
|
||||
|
||||
endif
|
||||
@@ -0,0 +1,27 @@
|
||||
Import('rtconfig')
|
||||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
src = []
|
||||
CPPPATH = [cwd]
|
||||
|
||||
if GetDepend('RT_UTEST_TC_USING_DFS_V2_AF_UNIX'):
|
||||
cases = []
|
||||
if GetDepend('RT_UTEST_DFS_V2_AF_UNIX_NODE'):
|
||||
cases += ['tc_af_unix_node.c']
|
||||
if GetDepend('RT_UTEST_DFS_V2_AF_UNIX_IPC'):
|
||||
cases += ['tc_af_unix_ipc.c']
|
||||
if GetDepend('RT_UTEST_DFS_V2_AF_UNIX_POLL'):
|
||||
cases += ['tc_af_unix_poll.c']
|
||||
if GetDepend('RT_UTEST_DFS_V2_AF_UNIX_NAMESPACE'):
|
||||
cases += ['tc_af_unix_namespace.c']
|
||||
if GetDepend('RT_UTEST_DFS_V2_AF_UNIX_RIGHTS'):
|
||||
cases += ['tc_af_unix_rights.c']
|
||||
if cases:
|
||||
src += ['tc_af_unix_common.c'] + cases
|
||||
|
||||
group = DefineGroup('utestcases', src,
|
||||
depend = ['RT_USING_UTESTCASES', 'RT_USING_DFS_V2', 'RT_USING_AF_UNIX'],
|
||||
CPPPATH = CPPPATH)
|
||||
|
||||
Return('group')
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2026, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef TC_AF_UNIX_H__
|
||||
#define TC_AF_UNIX_H__
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <dfs.h>
|
||||
#include <dfs_file.h>
|
||||
#include <rtthread.h>
|
||||
#include <utest.h>
|
||||
|
||||
#ifndef RT_UTEST_DFS_V2_AF_UNIX_DIR
|
||||
#define RT_UTEST_DFS_V2_AF_UNIX_DIR "/dev"
|
||||
#endif
|
||||
|
||||
#define TC_AF_UNIX_PATH_MAX 64
|
||||
|
||||
void tc_af_unix_path(char *buf, rt_size_t size, const char *name);
|
||||
void tc_af_unix_make_address(struct sockaddr_un *address, const char *path);
|
||||
void tc_af_unix_closesocket(int *fd);
|
||||
void tc_af_unix_close(int *fd);
|
||||
int tc_af_unix_send_rights(int socket_fd, const int *fds, size_t fd_count,
|
||||
char payload);
|
||||
int tc_af_unix_receive_rights(int socket_fd, int flags, size_t control_length,
|
||||
int *fds, size_t *fd_count, int *message_flags,
|
||||
char *payload);
|
||||
|
||||
#endif /* TC_AF_UNIX_H__ */
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2026, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "tc_af_unix.h"
|
||||
|
||||
void tc_af_unix_path(char *buf, rt_size_t size, const char *name)
|
||||
{
|
||||
rt_snprintf(buf, size, "%s/%s", RT_UTEST_DFS_V2_AF_UNIX_DIR, name);
|
||||
}
|
||||
|
||||
void tc_af_unix_make_address(struct sockaddr_un *address, const char *path)
|
||||
{
|
||||
rt_memset(address, 0, sizeof(*address));
|
||||
address->sa_family = AF_UNIX;
|
||||
rt_strncpy(address->sun_path, path, sizeof(address->sun_path) - 1);
|
||||
}
|
||||
|
||||
void tc_af_unix_closesocket(int *fd)
|
||||
{
|
||||
if (*fd >= 0)
|
||||
{
|
||||
closesocket(*fd);
|
||||
*fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void tc_af_unix_close(int *fd)
|
||||
{
|
||||
if (*fd >= 0)
|
||||
{
|
||||
close(*fd);
|
||||
*fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int tc_af_unix_send_rights(int socket_fd, const int *fds, size_t fd_count,
|
||||
char payload)
|
||||
{
|
||||
char control[CMSG_SPACE(2 * sizeof(int))];
|
||||
struct cmsghdr *cmsg;
|
||||
struct iovec iov;
|
||||
struct msghdr message;
|
||||
|
||||
rt_memset(&message, 0, sizeof(message));
|
||||
rt_memset(control, 0, sizeof(control));
|
||||
iov.iov_base = &payload;
|
||||
iov.iov_len = sizeof(payload);
|
||||
message.msg_iov = &iov;
|
||||
message.msg_iovlen = 1;
|
||||
message.msg_control = control;
|
||||
message.msg_controllen = CMSG_SPACE(fd_count * sizeof(int));
|
||||
cmsg = CMSG_FIRSTHDR(&message);
|
||||
cmsg->cmsg_len = CMSG_LEN(fd_count * sizeof(int));
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
rt_memcpy(CMSG_DATA(cmsg), fds, fd_count * sizeof(int));
|
||||
return sendmsg(socket_fd, &message, 0);
|
||||
}
|
||||
|
||||
int tc_af_unix_receive_rights(int socket_fd, int flags, size_t control_length,
|
||||
int *fds, size_t *fd_count, int *message_flags,
|
||||
char *payload)
|
||||
{
|
||||
char control[CMSG_SPACE(2 * sizeof(int))];
|
||||
int result;
|
||||
size_t count = 0;
|
||||
struct cmsghdr *cmsg;
|
||||
struct iovec iov;
|
||||
struct msghdr message;
|
||||
|
||||
rt_memset(&message, 0, sizeof(message));
|
||||
rt_memset(control, 0, sizeof(control));
|
||||
iov.iov_base = payload;
|
||||
iov.iov_len = sizeof(*payload);
|
||||
message.msg_iov = &iov;
|
||||
message.msg_iovlen = 1;
|
||||
message.msg_control = control_length != 0 ? control : RT_NULL;
|
||||
message.msg_controllen = control_length;
|
||||
|
||||
result = recvmsg(socket_fd, &message, flags);
|
||||
if (result >= 0)
|
||||
{
|
||||
cmsg = CMSG_FIRSTHDR(&message);
|
||||
if (cmsg != RT_NULL && cmsg->cmsg_level == SOL_SOCKET &&
|
||||
cmsg->cmsg_type == SCM_RIGHTS &&
|
||||
cmsg->cmsg_len >= CMSG_LEN(sizeof(int)))
|
||||
{
|
||||
count = (cmsg->cmsg_len - sizeof(*cmsg)) / sizeof(int);
|
||||
rt_memcpy(fds, CMSG_DATA(cmsg), count * sizeof(int));
|
||||
}
|
||||
*fd_count = count;
|
||||
*message_flags = message.msg_flags;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2026, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "tc_af_unix.h"
|
||||
|
||||
static void tc_af_unix_dgram_ipc(void)
|
||||
{
|
||||
char buffer[16];
|
||||
char client_path[TC_AF_UNIX_PATH_MAX];
|
||||
char server_path[TC_AF_UNIX_PATH_MAX];
|
||||
const char payload[] = "datagram";
|
||||
int client = -1;
|
||||
int error;
|
||||
int result;
|
||||
int server = -1;
|
||||
socklen_t source_length;
|
||||
struct sockaddr_un client_address;
|
||||
struct sockaddr_un server_address;
|
||||
struct sockaddr_un source_address;
|
||||
|
||||
tc_af_unix_path(server_path, sizeof(server_path), "utafudgs");
|
||||
tc_af_unix_path(client_path, sizeof(client_path), "utafudgc");
|
||||
(void)unlink(server_path);
|
||||
(void)unlink(client_path);
|
||||
tc_af_unix_make_address(&server_address, server_path);
|
||||
tc_af_unix_make_address(&client_address, client_path);
|
||||
|
||||
server = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
client = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
uassert_true(server >= 0);
|
||||
uassert_true(client >= 0);
|
||||
if (server < 0 || client < 0)
|
||||
{
|
||||
goto __exit;
|
||||
}
|
||||
|
||||
uassert_int_equal(bind(server, (struct sockaddr *)&server_address,
|
||||
sizeof(server_address)),
|
||||
0);
|
||||
uassert_int_equal(bind(client, (struct sockaddr *)&client_address,
|
||||
sizeof(client_address)),
|
||||
0);
|
||||
uassert_int_equal(connect(client, (struct sockaddr *)&server_address,
|
||||
sizeof(server_address)),
|
||||
0);
|
||||
uassert_int_equal(send(client, payload, sizeof(payload), 0),
|
||||
sizeof(payload));
|
||||
|
||||
source_length = sizeof(source_address);
|
||||
result = recvfrom(server, buffer, sizeof(buffer), 0,
|
||||
(struct sockaddr *)&source_address, &source_length);
|
||||
uassert_int_equal(result, sizeof(payload));
|
||||
uassert_buf_equal(buffer, payload, sizeof(payload));
|
||||
uassert_str_equal(source_address.sun_path, client_path);
|
||||
|
||||
result = fcntl(server, F_SETFL, O_NONBLOCK);
|
||||
uassert_int_equal(result, 0);
|
||||
result = recv(server, buffer, sizeof(buffer), 0);
|
||||
error = rt_get_errno();
|
||||
uassert_int_equal(result, -1);
|
||||
uassert_true(error == EAGAIN || error == EWOULDBLOCK);
|
||||
|
||||
result = listen(server, 1);
|
||||
error = rt_get_errno();
|
||||
uassert_int_equal(result, -1);
|
||||
uassert_int_equal(error, EOPNOTSUPP);
|
||||
|
||||
__exit:
|
||||
tc_af_unix_closesocket(&client);
|
||||
tc_af_unix_closesocket(&server);
|
||||
(void)unlink(client_path);
|
||||
(void)unlink(server_path);
|
||||
}
|
||||
|
||||
static void tc_af_unix_stream_ipc(void)
|
||||
{
|
||||
char accepted_path[TC_AF_UNIX_PATH_MAX];
|
||||
char buffer[16];
|
||||
const char request[] = "request";
|
||||
const char response[] = "response";
|
||||
int accepted = -1;
|
||||
int client = -1;
|
||||
int listener = -1;
|
||||
struct sockaddr_un address;
|
||||
struct sockaddr_un local_address;
|
||||
struct sockaddr_un peer_address;
|
||||
socklen_t local_length;
|
||||
socklen_t peer_length;
|
||||
|
||||
tc_af_unix_path(accepted_path, sizeof(accepted_path), "utafustr");
|
||||
(void)unlink(accepted_path);
|
||||
tc_af_unix_make_address(&address, accepted_path);
|
||||
|
||||
listener = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
client = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
uassert_true(listener >= 0);
|
||||
uassert_true(client >= 0);
|
||||
if (listener < 0 || client < 0)
|
||||
{
|
||||
goto __exit;
|
||||
}
|
||||
|
||||
uassert_int_equal(bind(listener, (struct sockaddr *)&address,
|
||||
sizeof(address)),
|
||||
0);
|
||||
uassert_int_equal(listen(listener, 2), 0);
|
||||
uassert_int_equal(connect(client, (struct sockaddr *)&address,
|
||||
sizeof(address)),
|
||||
0);
|
||||
accepted = accept(listener, RT_NULL, RT_NULL);
|
||||
uassert_true(accepted >= 0);
|
||||
if (accepted < 0)
|
||||
{
|
||||
goto __exit;
|
||||
}
|
||||
|
||||
local_length = sizeof(local_address);
|
||||
peer_length = sizeof(peer_address);
|
||||
uassert_int_equal(getsockname(accepted, (struct sockaddr *)&local_address,
|
||||
&local_length),
|
||||
0);
|
||||
uassert_int_equal(getpeername(client, (struct sockaddr *)&peer_address,
|
||||
&peer_length),
|
||||
0);
|
||||
uassert_str_equal(local_address.sun_path, accepted_path);
|
||||
uassert_str_equal(peer_address.sun_path, accepted_path);
|
||||
|
||||
uassert_int_equal(send(client, request, sizeof(request), 0),
|
||||
sizeof(request));
|
||||
uassert_int_equal(recv(accepted, buffer, sizeof(buffer), 0),
|
||||
sizeof(request));
|
||||
uassert_buf_equal(buffer, request, sizeof(request));
|
||||
uassert_int_equal(send(accepted, response, sizeof(response), 0),
|
||||
sizeof(response));
|
||||
uassert_int_equal(recv(client, buffer, sizeof(buffer), 0),
|
||||
sizeof(response));
|
||||
uassert_buf_equal(buffer, response, sizeof(response));
|
||||
|
||||
uassert_int_equal(shutdown(client, SHUT_WR), 0);
|
||||
uassert_int_equal(recv(accepted, buffer, sizeof(buffer), 0), 0);
|
||||
|
||||
__exit:
|
||||
tc_af_unix_closesocket(&accepted);
|
||||
tc_af_unix_closesocket(&client);
|
||||
tc_af_unix_closesocket(&listener);
|
||||
(void)unlink(accepted_path);
|
||||
}
|
||||
|
||||
static void tc_af_unix_socketpair_msg(void)
|
||||
{
|
||||
char buffer[8];
|
||||
char first_part[3];
|
||||
char second_part[3];
|
||||
const char payload[] = "pair";
|
||||
int sockets[2] = { -1, -1 };
|
||||
int type_index;
|
||||
int types[2] = { SOCK_DGRAM, SOCK_STREAM };
|
||||
struct iovec receive_iov[2];
|
||||
struct iovec send_iov[2];
|
||||
struct msghdr receive_message;
|
||||
struct msghdr send_message;
|
||||
|
||||
for (type_index = 0; type_index < 2; type_index++)
|
||||
{
|
||||
uassert_int_equal(socketpair(AF_UNIX, types[type_index], 0, sockets),
|
||||
0);
|
||||
uassert_int_equal(send(sockets[0], payload, sizeof(payload), 0),
|
||||
sizeof(payload));
|
||||
uassert_int_equal(recv(sockets[1], buffer, sizeof(buffer), 0),
|
||||
sizeof(payload));
|
||||
uassert_buf_equal(buffer, payload, sizeof(payload));
|
||||
|
||||
rt_memset(&send_message, 0, sizeof(send_message));
|
||||
send_iov[0].iov_base = (void *)payload;
|
||||
send_iov[0].iov_len = 2;
|
||||
send_iov[1].iov_base = (void *)(payload + 2);
|
||||
send_iov[1].iov_len = sizeof(payload) - 2;
|
||||
send_message.msg_iov = send_iov;
|
||||
send_message.msg_iovlen = 2;
|
||||
send_message.msg_name = RT_NULL;
|
||||
send_message.msg_namelen = sizeof(struct sockaddr_un);
|
||||
uassert_int_equal(sendmsg(sockets[0], &send_message, 0),
|
||||
sizeof(payload));
|
||||
|
||||
rt_memset(&receive_message, 0, sizeof(receive_message));
|
||||
receive_iov[0].iov_base = first_part;
|
||||
receive_iov[0].iov_len = sizeof(first_part);
|
||||
receive_iov[1].iov_base = second_part;
|
||||
receive_iov[1].iov_len = sizeof(second_part);
|
||||
receive_message.msg_iov = receive_iov;
|
||||
receive_message.msg_iovlen = 2;
|
||||
uassert_int_equal(recvmsg(sockets[1], &receive_message, 0),
|
||||
sizeof(payload));
|
||||
uassert_buf_equal(first_part, payload, sizeof(first_part));
|
||||
uassert_buf_equal(second_part, payload + sizeof(first_part),
|
||||
sizeof(payload) - sizeof(first_part));
|
||||
|
||||
send_message.msg_control = RT_NULL;
|
||||
send_message.msg_controllen = CMSG_SPACE(sizeof(int));
|
||||
uassert_int_equal(sendmsg(sockets[0], &send_message, 0), -1);
|
||||
uassert_int_equal(rt_get_errno(), EINVAL);
|
||||
|
||||
tc_af_unix_closesocket(&sockets[0]);
|
||||
tc_af_unix_closesocket(&sockets[1]);
|
||||
}
|
||||
}
|
||||
|
||||
static void tc_af_unix_connect_missing(void)
|
||||
{
|
||||
char path[TC_AF_UNIX_PATH_MAX];
|
||||
int client = -1;
|
||||
int error;
|
||||
int result;
|
||||
struct sockaddr_un address;
|
||||
|
||||
tc_af_unix_path(path, sizeof(path), "utafumiss");
|
||||
(void)unlink(path);
|
||||
tc_af_unix_make_address(&address, path);
|
||||
|
||||
client = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
uassert_true(client >= 0);
|
||||
if (client < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
result = connect(client, (struct sockaddr *)&address, sizeof(address));
|
||||
error = rt_get_errno();
|
||||
uassert_int_equal(result, -1);
|
||||
uassert_int_equal(error, ENOENT);
|
||||
|
||||
tc_af_unix_closesocket(&client);
|
||||
}
|
||||
|
||||
static void tc_af_unix_pending_close(void)
|
||||
{
|
||||
char path[TC_AF_UNIX_PATH_MAX];
|
||||
const char payload[] = "pending";
|
||||
int client = -1;
|
||||
int error;
|
||||
int listener = -1;
|
||||
int result;
|
||||
struct sockaddr_un address;
|
||||
|
||||
tc_af_unix_path(path, sizeof(path), "utafupend");
|
||||
(void)unlink(path);
|
||||
tc_af_unix_make_address(&address, path);
|
||||
|
||||
listener = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
client = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
uassert_true(listener >= 0);
|
||||
uassert_true(client >= 0);
|
||||
if (listener < 0 || client < 0)
|
||||
{
|
||||
goto __exit;
|
||||
}
|
||||
|
||||
uassert_int_equal(bind(listener, (struct sockaddr *)&address,
|
||||
sizeof(address)),
|
||||
0);
|
||||
uassert_int_equal(listen(listener, 1), 0);
|
||||
uassert_int_equal(connect(client, (struct sockaddr *)&address,
|
||||
sizeof(address)),
|
||||
0);
|
||||
tc_af_unix_closesocket(&listener);
|
||||
|
||||
result = send(client, payload, sizeof(payload), 0);
|
||||
error = rt_get_errno();
|
||||
uassert_int_equal(result, -1);
|
||||
uassert_true(error == ENOTCONN || error == EPIPE);
|
||||
|
||||
__exit:
|
||||
tc_af_unix_closesocket(&client);
|
||||
tc_af_unix_closesocket(&listener);
|
||||
(void)unlink(path);
|
||||
}
|
||||
|
||||
static void tc_af_unix_ipc_testcase(void)
|
||||
{
|
||||
UTEST_UNIT_RUN(tc_af_unix_dgram_ipc);
|
||||
UTEST_UNIT_RUN(tc_af_unix_stream_ipc);
|
||||
UTEST_UNIT_RUN(tc_af_unix_socketpair_msg);
|
||||
UTEST_UNIT_RUN(tc_af_unix_connect_missing);
|
||||
UTEST_UNIT_RUN(tc_af_unix_pending_close);
|
||||
}
|
||||
|
||||
UTEST_TC_EXPORT(tc_af_unix_ipc_testcase, "components.dfs.v2.af_unix.ipc",
|
||||
RT_NULL, RT_NULL, 15);
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2026, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "tc_af_unix.h"
|
||||
|
||||
#define TC_AF_UNIX_NAMESPACE_REPEAT 8
|
||||
|
||||
static void tc_af_unix_pathname_lifetime(void)
|
||||
{
|
||||
char path[TC_AF_UNIX_PATH_MAX];
|
||||
int error;
|
||||
int first = -1;
|
||||
int result;
|
||||
int second = -1;
|
||||
struct sockaddr_un address;
|
||||
struct stat file_stat;
|
||||
|
||||
tc_af_unix_path(path, sizeof(path), "utafupath");
|
||||
(void)unlink(path);
|
||||
tc_af_unix_make_address(&address, path);
|
||||
|
||||
first = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
uassert_true(first >= 0);
|
||||
if (first < 0)
|
||||
{
|
||||
goto __exit;
|
||||
}
|
||||
uassert_int_equal(bind(first, (struct sockaddr *)&address, sizeof(address)),
|
||||
0);
|
||||
uassert_int_equal(stat(path, &file_stat), 0);
|
||||
uassert_true(S_ISSOCK(file_stat.st_mode));
|
||||
|
||||
tc_af_unix_closesocket(&first);
|
||||
uassert_int_equal(stat(path, &file_stat), 0);
|
||||
uassert_true(S_ISSOCK(file_stat.st_mode));
|
||||
|
||||
second = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
uassert_true(second >= 0);
|
||||
if (second < 0)
|
||||
{
|
||||
goto __exit;
|
||||
}
|
||||
result = bind(second, (struct sockaddr *)&address, sizeof(address));
|
||||
error = rt_get_errno();
|
||||
uassert_int_equal(result, -1);
|
||||
uassert_int_equal(error, EADDRINUSE);
|
||||
|
||||
uassert_int_equal(unlink(path), 0);
|
||||
uassert_int_equal(bind(second, (struct sockaddr *)&address,
|
||||
sizeof(address)),
|
||||
0);
|
||||
|
||||
__exit:
|
||||
tc_af_unix_closesocket(&second);
|
||||
tc_af_unix_closesocket(&first);
|
||||
(void)unlink(path);
|
||||
}
|
||||
|
||||
static void tc_af_unix_lookup_after_close(void)
|
||||
{
|
||||
char path[TC_AF_UNIX_PATH_MAX];
|
||||
int client = -1;
|
||||
int error;
|
||||
int result;
|
||||
int server = -1;
|
||||
struct sockaddr_un address;
|
||||
|
||||
tc_af_unix_path(path, sizeof(path), "utafuref");
|
||||
(void)unlink(path);
|
||||
tc_af_unix_make_address(&address, path);
|
||||
|
||||
server = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
client = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
uassert_true(server >= 0);
|
||||
uassert_true(client >= 0);
|
||||
if (server < 0 || client < 0)
|
||||
{
|
||||
goto __exit;
|
||||
}
|
||||
|
||||
uassert_int_equal(bind(server, (struct sockaddr *)&address,
|
||||
sizeof(address)),
|
||||
0);
|
||||
uassert_int_equal(listen(server, 1), 0);
|
||||
tc_af_unix_closesocket(&server);
|
||||
|
||||
result = connect(client, (struct sockaddr *)&address, sizeof(address));
|
||||
error = rt_get_errno();
|
||||
uassert_int_equal(result, -1);
|
||||
uassert_int_equal(error, ECONNREFUSED);
|
||||
|
||||
uassert_int_equal(unlink(path), 0);
|
||||
result = connect(client, (struct sockaddr *)&address, sizeof(address));
|
||||
error = rt_get_errno();
|
||||
uassert_int_equal(result, -1);
|
||||
uassert_int_equal(error, ENOENT);
|
||||
|
||||
__exit:
|
||||
tc_af_unix_closesocket(&client);
|
||||
tc_af_unix_closesocket(&server);
|
||||
(void)unlink(path);
|
||||
}
|
||||
|
||||
static void tc_af_unix_namespace_reuse(void)
|
||||
{
|
||||
char name[16];
|
||||
char path[TC_AF_UNIX_PATH_MAX];
|
||||
int client = -1;
|
||||
int error;
|
||||
int index;
|
||||
int result;
|
||||
int sock = -1;
|
||||
struct sockaddr_un address;
|
||||
|
||||
for (index = 0; index < TC_AF_UNIX_NAMESPACE_REPEAT; index++)
|
||||
{
|
||||
rt_snprintf(name, sizeof(name), "utafuns%d", index);
|
||||
tc_af_unix_path(path, sizeof(path), name);
|
||||
(void)unlink(path);
|
||||
tc_af_unix_make_address(&address, path);
|
||||
|
||||
sock = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
uassert_true(sock >= 0);
|
||||
if (sock < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
uassert_int_equal(bind(sock, (struct sockaddr *)&address,
|
||||
sizeof(address)),
|
||||
0);
|
||||
tc_af_unix_closesocket(&sock);
|
||||
uassert_int_equal(unlink(path), 0);
|
||||
}
|
||||
|
||||
tc_af_unix_path(path, sizeof(path), "utafuns0");
|
||||
tc_af_unix_make_address(&address, path);
|
||||
client = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
uassert_true(client >= 0);
|
||||
if (client < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
result = connect(client, (struct sockaddr *)&address, sizeof(address));
|
||||
error = rt_get_errno();
|
||||
uassert_int_equal(result, -1);
|
||||
uassert_int_equal(error, ENOENT);
|
||||
tc_af_unix_closesocket(&client);
|
||||
|
||||
tc_af_unix_path(path, sizeof(path), "utafunew");
|
||||
(void)unlink(path);
|
||||
tc_af_unix_make_address(&address, path);
|
||||
sock = socket(AF_UNIX, SOCK_DGRAM, 0);
|
||||
uassert_true(sock >= 0);
|
||||
if (sock < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
uassert_int_equal(bind(sock, (struct sockaddr *)&address, sizeof(address)),
|
||||
0);
|
||||
tc_af_unix_closesocket(&sock);
|
||||
(void)unlink(path);
|
||||
}
|
||||
|
||||
static void tc_af_unix_namespace_testcase(void)
|
||||
{
|
||||
UTEST_UNIT_RUN(tc_af_unix_pathname_lifetime);
|
||||
UTEST_UNIT_RUN(tc_af_unix_lookup_after_close);
|
||||
UTEST_UNIT_RUN(tc_af_unix_namespace_reuse);
|
||||
}
|
||||
|
||||
UTEST_TC_EXPORT(tc_af_unix_namespace_testcase,
|
||||
"components.dfs.v2.af_unix.namespace", RT_NULL, RT_NULL, 15);
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2026, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "tc_af_unix.h"
|
||||
|
||||
static void tc_af_unix_mknod_socket(void)
|
||||
{
|
||||
char path[TC_AF_UNIX_PATH_MAX];
|
||||
int result;
|
||||
struct stat file_stat;
|
||||
|
||||
tc_af_unix_path(path, sizeof(path), "utafumkn");
|
||||
(void)unlink(path);
|
||||
|
||||
result = dfs_file_mknod(path, FT_SOCKET, S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
uassert_int_equal(result, 0);
|
||||
if (result != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uassert_int_equal(stat(path, &file_stat), 0);
|
||||
uassert_true(S_ISSOCK(file_stat.st_mode));
|
||||
|
||||
result = dfs_file_mknod(path, FT_SOCKET, S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
uassert_int_equal(result, -EEXIST);
|
||||
|
||||
uassert_int_equal(unlink(path), 0);
|
||||
uassert_true(stat(path, &file_stat) < 0);
|
||||
}
|
||||
|
||||
static void tc_af_unix_file_refs(void)
|
||||
{
|
||||
char data[4];
|
||||
int installed = -1;
|
||||
int invalid_fd = -1;
|
||||
int pipe_fds[2] = { -1, -1 };
|
||||
struct dfs_file *files[1];
|
||||
|
||||
files[0] = RT_NULL;
|
||||
uassert_int_equal(dfs_file_get_refs(&invalid_fd, 1, files), -EBADF);
|
||||
uassert_true(files[0] == RT_NULL);
|
||||
|
||||
uassert_int_equal(pipe(pipe_fds), 0);
|
||||
if (pipe_fds[0] < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
uassert_int_equal(write(pipe_fds[1], "abcd", 4), 4);
|
||||
uassert_int_equal(dfs_file_get_refs(&pipe_fds[0], 1, files), 0);
|
||||
uassert_true(files[0] != RT_NULL);
|
||||
|
||||
dfs_file_put_ref(files[0]);
|
||||
uassert_int_equal(read(pipe_fds[0], data, 2), 2);
|
||||
uassert_buf_equal(data, "ab", 2);
|
||||
|
||||
uassert_int_equal(dfs_file_get_refs(&pipe_fds[0], 1, files), 0);
|
||||
uassert_int_equal(dfs_file_install_refs(files, 1, &installed), 0);
|
||||
uassert_true(installed >= 0);
|
||||
uassert_int_equal(read(installed, data, 2), 2);
|
||||
uassert_buf_equal(data, "cd", 2);
|
||||
|
||||
tc_af_unix_close(&installed);
|
||||
tc_af_unix_close(&pipe_fds[0]);
|
||||
tc_af_unix_close(&pipe_fds[1]);
|
||||
}
|
||||
|
||||
static void tc_af_unix_node_testcase(void)
|
||||
{
|
||||
UTEST_UNIT_RUN(tc_af_unix_mknod_socket);
|
||||
UTEST_UNIT_RUN(tc_af_unix_file_refs);
|
||||
}
|
||||
|
||||
UTEST_TC_EXPORT(tc_af_unix_node_testcase, "components.dfs.v2.af_unix.node",
|
||||
RT_NULL, RT_NULL, 10);
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2026, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "tc_af_unix.h"
|
||||
|
||||
#define TC_AF_UNIX_POLL_STACK 4096
|
||||
#define TC_AF_UNIX_POLL_PRIO 20
|
||||
|
||||
struct tc_af_unix_poll_ctx
|
||||
{
|
||||
int fd;
|
||||
int result;
|
||||
short revents;
|
||||
struct rt_event done;
|
||||
};
|
||||
|
||||
static void tc_af_unix_poll_entry(void *parameter)
|
||||
{
|
||||
struct pollfd poll_fd;
|
||||
struct tc_af_unix_poll_ctx *ctx;
|
||||
|
||||
ctx = (struct tc_af_unix_poll_ctx *)parameter;
|
||||
poll_fd.fd = ctx->fd;
|
||||
poll_fd.events = POLLIN;
|
||||
poll_fd.revents = 0;
|
||||
ctx->result = poll(&poll_fd, 1, 3000);
|
||||
ctx->revents = poll_fd.revents;
|
||||
rt_event_send(&ctx->done, 0x1);
|
||||
}
|
||||
|
||||
static void tc_af_unix_stream_readiness(void)
|
||||
{
|
||||
char buffer[8];
|
||||
char *fill;
|
||||
const char payload[] = "poll";
|
||||
int result;
|
||||
int sockets[2] = { -1, -1 };
|
||||
rt_size_t sent = 0;
|
||||
struct pollfd poll_fd;
|
||||
|
||||
result = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets);
|
||||
uassert_int_equal(result, 0);
|
||||
if (result < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
poll_fd.fd = sockets[1];
|
||||
poll_fd.events = POLLIN | POLLOUT;
|
||||
poll_fd.revents = 0;
|
||||
uassert_int_equal(poll(&poll_fd, 1, 0), 1);
|
||||
uassert_true((poll_fd.revents & POLLOUT) != 0);
|
||||
uassert_true((poll_fd.revents & POLLIN) == 0);
|
||||
|
||||
uassert_int_equal(send(sockets[0], payload, sizeof(payload), 0),
|
||||
sizeof(payload));
|
||||
poll_fd.revents = 0;
|
||||
uassert_int_equal(poll(&poll_fd, 1, 0), 1);
|
||||
uassert_true((poll_fd.revents & POLLIN) != 0);
|
||||
uassert_int_equal(recv(sockets[1], buffer, sizeof(buffer), 0),
|
||||
sizeof(payload));
|
||||
|
||||
fill = (char *)rt_malloc(AF_UNIX_STREAM_BUFFER_SIZE);
|
||||
uassert_true(fill != RT_NULL);
|
||||
if (fill != RT_NULL)
|
||||
{
|
||||
rt_memset(fill, 'x', AF_UNIX_STREAM_BUFFER_SIZE);
|
||||
uassert_int_equal(fcntl(sockets[0], F_SETFL, O_NONBLOCK), 0);
|
||||
while (sent < AF_UNIX_STREAM_BUFFER_SIZE)
|
||||
{
|
||||
result = send(sockets[0], fill + sent,
|
||||
AF_UNIX_STREAM_BUFFER_SIZE - sent, 0);
|
||||
if (result <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
sent += (rt_size_t)result;
|
||||
}
|
||||
uassert_int_equal(sent, AF_UNIX_STREAM_BUFFER_SIZE);
|
||||
|
||||
poll_fd.fd = sockets[0];
|
||||
poll_fd.events = POLLOUT;
|
||||
poll_fd.revents = 0;
|
||||
uassert_int_equal(poll(&poll_fd, 1, 0), 0);
|
||||
|
||||
uassert_int_equal(recv(sockets[1], fill, 64, 0), 64);
|
||||
poll_fd.revents = 0;
|
||||
uassert_int_equal(poll(&poll_fd, 1, 0), 1);
|
||||
uassert_true((poll_fd.revents & POLLOUT) != 0);
|
||||
rt_free(fill);
|
||||
}
|
||||
|
||||
uassert_int_equal(shutdown(sockets[0], SHUT_WR), 0);
|
||||
poll_fd.fd = sockets[1];
|
||||
poll_fd.events = POLLIN;
|
||||
poll_fd.revents = 0;
|
||||
uassert_int_equal(poll(&poll_fd, 1, 0), 1);
|
||||
uassert_true((poll_fd.revents & POLLIN) != 0);
|
||||
|
||||
tc_af_unix_closesocket(&sockets[0]);
|
||||
tc_af_unix_closesocket(&sockets[1]);
|
||||
}
|
||||
|
||||
static void tc_af_unix_dgram_readiness(void)
|
||||
{
|
||||
char buffer[2];
|
||||
const char payload[] = "x";
|
||||
int error;
|
||||
int index;
|
||||
int result;
|
||||
int sockets[2] = { -1, -1 };
|
||||
struct pollfd poll_fd;
|
||||
|
||||
result = socketpair(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK, 0, sockets);
|
||||
uassert_int_equal(result, 0);
|
||||
if (result < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
poll_fd.fd = sockets[1];
|
||||
poll_fd.events = POLLIN;
|
||||
poll_fd.revents = 0;
|
||||
uassert_int_equal(poll(&poll_fd, 1, 0), 0);
|
||||
|
||||
for (index = 0; index < AF_UNIX_DGRAM_QUEUE_LEN; index++)
|
||||
{
|
||||
uassert_int_equal(send(sockets[0], payload, sizeof(payload), 0),
|
||||
sizeof(payload));
|
||||
}
|
||||
result = send(sockets[0], payload, sizeof(payload), 0);
|
||||
error = rt_get_errno();
|
||||
uassert_int_equal(result, -1);
|
||||
uassert_int_equal(error, EAGAIN);
|
||||
|
||||
poll_fd.revents = 0;
|
||||
uassert_int_equal(poll(&poll_fd, 1, 0), 1);
|
||||
uassert_true((poll_fd.revents & POLLIN) != 0);
|
||||
uassert_int_equal(recv(sockets[1], buffer, sizeof(buffer), 0),
|
||||
sizeof(payload));
|
||||
uassert_int_equal(send(sockets[0], payload, sizeof(payload), 0),
|
||||
sizeof(payload));
|
||||
|
||||
result = send(sockets[0], payload, AF_UNIX_DGRAM_MAX_SIZE + 1, 0);
|
||||
error = rt_get_errno();
|
||||
uassert_int_equal(result, -1);
|
||||
uassert_int_equal(error, EMSGSIZE);
|
||||
|
||||
tc_af_unix_closesocket(&sockets[0]);
|
||||
tc_af_unix_closesocket(&sockets[1]);
|
||||
}
|
||||
|
||||
static void tc_af_unix_poll_peer_close(void)
|
||||
{
|
||||
int sockets[2] = { -1, -1 };
|
||||
rt_err_t wait;
|
||||
rt_thread_t thread;
|
||||
rt_uint32_t recved;
|
||||
struct tc_af_unix_poll_ctx ctx;
|
||||
|
||||
uassert_int_equal(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets), 0);
|
||||
if (sockets[0] < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
rt_memset(&ctx, 0, sizeof(ctx));
|
||||
ctx.fd = sockets[0];
|
||||
ctx.result = -2;
|
||||
uassert_int_equal(rt_event_init(&ctx.done, "utafup", RT_IPC_FLAG_PRIO),
|
||||
RT_EOK);
|
||||
|
||||
thread = rt_thread_create("utafup", tc_af_unix_poll_entry, &ctx,
|
||||
TC_AF_UNIX_POLL_STACK, TC_AF_UNIX_POLL_PRIO, 10);
|
||||
uassert_true(thread != RT_NULL);
|
||||
if (thread == RT_NULL)
|
||||
{
|
||||
rt_event_detach(&ctx.done);
|
||||
tc_af_unix_closesocket(&sockets[0]);
|
||||
tc_af_unix_closesocket(&sockets[1]);
|
||||
return;
|
||||
}
|
||||
|
||||
rt_thread_startup(thread);
|
||||
rt_thread_mdelay(50);
|
||||
tc_af_unix_closesocket(&sockets[1]);
|
||||
|
||||
wait = rt_event_recv(&ctx.done, 0x1,
|
||||
RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR,
|
||||
rt_tick_from_millisecond(3000), &recved);
|
||||
uassert_int_equal(wait, RT_EOK);
|
||||
uassert_int_equal(ctx.result, 1);
|
||||
uassert_true((ctx.revents & (POLLIN | POLLHUP)) != 0);
|
||||
|
||||
rt_event_detach(&ctx.done);
|
||||
tc_af_unix_closesocket(&sockets[0]);
|
||||
tc_af_unix_closesocket(&sockets[1]);
|
||||
}
|
||||
|
||||
static void tc_af_unix_poll_testcase(void)
|
||||
{
|
||||
UTEST_UNIT_RUN(tc_af_unix_stream_readiness);
|
||||
UTEST_UNIT_RUN(tc_af_unix_dgram_readiness);
|
||||
UTEST_UNIT_RUN(tc_af_unix_poll_peer_close);
|
||||
}
|
||||
|
||||
UTEST_TC_EXPORT(tc_af_unix_poll_testcase, "components.dfs.v2.af_unix.poll",
|
||||
RT_NULL, RT_NULL, 20);
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2026, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "tc_af_unix.h"
|
||||
|
||||
static void tc_af_unix_rights_transfer(void)
|
||||
{
|
||||
char data;
|
||||
char payload;
|
||||
int message_flags;
|
||||
int pipe_fds[2][2];
|
||||
int received_fds[2];
|
||||
int send_fds[2];
|
||||
int sockets[2];
|
||||
int type_index;
|
||||
int types[2] = { SOCK_DGRAM, SOCK_STREAM };
|
||||
size_t fd_count;
|
||||
|
||||
for (type_index = 0; type_index < 2; type_index++)
|
||||
{
|
||||
received_fds[0] = -1;
|
||||
received_fds[1] = -1;
|
||||
pipe_fds[0][0] = -1;
|
||||
pipe_fds[0][1] = -1;
|
||||
pipe_fds[1][0] = -1;
|
||||
pipe_fds[1][1] = -1;
|
||||
sockets[0] = -1;
|
||||
sockets[1] = -1;
|
||||
|
||||
uassert_int_equal(socketpair(AF_UNIX, types[type_index], 0, sockets),
|
||||
0);
|
||||
uassert_int_equal(pipe(pipe_fds[0]), 0);
|
||||
uassert_int_equal(pipe(pipe_fds[1]), 0);
|
||||
if (sockets[0] < 0 || pipe_fds[0][0] < 0 || pipe_fds[1][0] < 0)
|
||||
{
|
||||
goto __iteration_exit;
|
||||
}
|
||||
|
||||
uassert_int_equal(write(pipe_fds[0][1], "a", 1), 1);
|
||||
uassert_int_equal(write(pipe_fds[1][1], "b", 1), 1);
|
||||
send_fds[0] = pipe_fds[0][0];
|
||||
send_fds[1] = pipe_fds[1][0];
|
||||
uassert_int_equal(tc_af_unix_send_rights(sockets[0], send_fds, 2, 'r'),
|
||||
1);
|
||||
tc_af_unix_close(&pipe_fds[0][0]);
|
||||
tc_af_unix_close(&pipe_fds[1][0]);
|
||||
|
||||
fd_count = 0;
|
||||
message_flags = 0;
|
||||
uassert_int_equal(tc_af_unix_receive_rights(
|
||||
sockets[1], 0, CMSG_SPACE(2 * sizeof(int)),
|
||||
received_fds, &fd_count, &message_flags,
|
||||
&payload),
|
||||
1);
|
||||
uassert_int_equal(payload, 'r');
|
||||
uassert_int_equal(fd_count, 2);
|
||||
uassert_int_equal(message_flags & MSG_CTRUNC, 0);
|
||||
uassert_int_equal(read(received_fds[0], &data, 1), 1);
|
||||
uassert_int_equal(data, 'a');
|
||||
uassert_int_equal(read(received_fds[1], &data, 1), 1);
|
||||
uassert_int_equal(data, 'b');
|
||||
|
||||
__iteration_exit:
|
||||
tc_af_unix_close(&received_fds[0]);
|
||||
tc_af_unix_close(&received_fds[1]);
|
||||
tc_af_unix_close(&pipe_fds[0][0]);
|
||||
tc_af_unix_close(&pipe_fds[0][1]);
|
||||
tc_af_unix_close(&pipe_fds[1][0]);
|
||||
tc_af_unix_close(&pipe_fds[1][1]);
|
||||
tc_af_unix_closesocket(&sockets[0]);
|
||||
tc_af_unix_closesocket(&sockets[1]);
|
||||
}
|
||||
}
|
||||
|
||||
static void tc_af_unix_rights_truncation(void)
|
||||
{
|
||||
char payload;
|
||||
int message_flags = 0;
|
||||
int pipe_fds[2][2] = { { -1, -1 }, { -1, -1 } };
|
||||
int received_fds[2] = { -1, -1 };
|
||||
int send_fds[2];
|
||||
int sockets[2] = { -1, -1 };
|
||||
size_t fd_count = 0;
|
||||
|
||||
uassert_int_equal(socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets), 0);
|
||||
uassert_int_equal(pipe(pipe_fds[0]), 0);
|
||||
uassert_int_equal(pipe(pipe_fds[1]), 0);
|
||||
if (sockets[0] < 0 || pipe_fds[0][0] < 0 || pipe_fds[1][0] < 0)
|
||||
{
|
||||
goto __exit;
|
||||
}
|
||||
|
||||
send_fds[0] = pipe_fds[0][0];
|
||||
send_fds[1] = pipe_fds[1][0];
|
||||
uassert_int_equal(tc_af_unix_send_rights(sockets[0], send_fds, 2, 't'), 1);
|
||||
uassert_int_equal(tc_af_unix_receive_rights(sockets[1], 0, 0, received_fds,
|
||||
&fd_count, &message_flags,
|
||||
&payload),
|
||||
1);
|
||||
uassert_int_equal(fd_count, 0);
|
||||
uassert_true((message_flags & MSG_CTRUNC) != 0);
|
||||
|
||||
__exit:
|
||||
tc_af_unix_close(&received_fds[0]);
|
||||
tc_af_unix_close(&pipe_fds[0][0]);
|
||||
tc_af_unix_close(&pipe_fds[0][1]);
|
||||
tc_af_unix_close(&pipe_fds[1][0]);
|
||||
tc_af_unix_close(&pipe_fds[1][1]);
|
||||
tc_af_unix_closesocket(&sockets[0]);
|
||||
tc_af_unix_closesocket(&sockets[1]);
|
||||
}
|
||||
|
||||
static void tc_af_unix_rights_peek_and_invalid(void)
|
||||
{
|
||||
char payload;
|
||||
int error;
|
||||
int invalid_fd = -1;
|
||||
int message_flags = 0;
|
||||
int pipe_fds[2] = { -1, -1 };
|
||||
int received_fds[2] = { -1, -1 };
|
||||
int result;
|
||||
int sockets[2] = { -1, -1 };
|
||||
size_t fd_count = 0;
|
||||
|
||||
uassert_int_equal(socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0,
|
||||
sockets),
|
||||
0);
|
||||
uassert_int_equal(pipe(pipe_fds), 0);
|
||||
if (sockets[0] < 0 || pipe_fds[0] < 0)
|
||||
{
|
||||
goto __exit;
|
||||
}
|
||||
|
||||
result = tc_af_unix_send_rights(sockets[0], &invalid_fd, 1, 'x');
|
||||
error = rt_get_errno();
|
||||
uassert_int_equal(result, -1);
|
||||
uassert_int_equal(error, EBADF);
|
||||
|
||||
uassert_int_equal(tc_af_unix_send_rights(sockets[0], pipe_fds, 1, 'p'), 1);
|
||||
fd_count = 0;
|
||||
uassert_int_equal(tc_af_unix_receive_rights(
|
||||
sockets[1], MSG_PEEK, CMSG_SPACE(sizeof(int)),
|
||||
received_fds, &fd_count, &message_flags, &payload),
|
||||
1);
|
||||
uassert_int_equal(payload, 'p');
|
||||
uassert_int_equal(fd_count, 0);
|
||||
|
||||
fd_count = 0;
|
||||
message_flags = 0;
|
||||
uassert_int_equal(tc_af_unix_receive_rights(
|
||||
sockets[1], 0, CMSG_SPACE(sizeof(int)),
|
||||
received_fds, &fd_count, &message_flags, &payload),
|
||||
1);
|
||||
uassert_int_equal(fd_count, 1);
|
||||
|
||||
__exit:
|
||||
tc_af_unix_close(&received_fds[0]);
|
||||
tc_af_unix_close(&pipe_fds[0]);
|
||||
tc_af_unix_close(&pipe_fds[1]);
|
||||
tc_af_unix_closesocket(&sockets[0]);
|
||||
tc_af_unix_closesocket(&sockets[1]);
|
||||
}
|
||||
|
||||
static void tc_af_unix_rights_reject_socket(void)
|
||||
{
|
||||
char payload;
|
||||
int carrier[2] = { -1, -1 };
|
||||
int error;
|
||||
int message_flags = 0;
|
||||
int passed[2] = { -1, -1 };
|
||||
int received_fds[2] = { -1, -1 };
|
||||
int result;
|
||||
size_t fd_count = 0;
|
||||
|
||||
uassert_int_equal(socketpair(AF_UNIX, SOCK_STREAM, 0, carrier), 0);
|
||||
uassert_int_equal(socketpair(AF_UNIX, SOCK_DGRAM, 0, passed), 0);
|
||||
if (carrier[0] < 0 || passed[0] < 0)
|
||||
{
|
||||
goto __exit;
|
||||
}
|
||||
|
||||
result = tc_af_unix_send_rights(carrier[0], &passed[1], 1, 's');
|
||||
error = rt_get_errno();
|
||||
uassert_int_equal(result, -1);
|
||||
uassert_int_equal(error, EOPNOTSUPP);
|
||||
|
||||
result = tc_af_unix_receive_rights(carrier[1], MSG_DONTWAIT,
|
||||
CMSG_SPACE(sizeof(int)), received_fds,
|
||||
&fd_count, &message_flags, &payload);
|
||||
error = rt_get_errno();
|
||||
uassert_int_equal(result, -1);
|
||||
uassert_true(error == EAGAIN || error == EWOULDBLOCK);
|
||||
uassert_int_equal(fd_count, 0);
|
||||
|
||||
uassert_int_equal(send(passed[0], "q", 1, 0), 1);
|
||||
uassert_int_equal(recv(passed[1], &payload, 1, 0), 1);
|
||||
uassert_int_equal(payload, 'q');
|
||||
|
||||
__exit:
|
||||
tc_af_unix_close(&received_fds[0]);
|
||||
tc_af_unix_closesocket(&passed[0]);
|
||||
tc_af_unix_closesocket(&passed[1]);
|
||||
tc_af_unix_closesocket(&carrier[0]);
|
||||
tc_af_unix_closesocket(&carrier[1]);
|
||||
}
|
||||
|
||||
static void tc_af_unix_rights_testcase(void)
|
||||
{
|
||||
UTEST_UNIT_RUN(tc_af_unix_rights_transfer);
|
||||
UTEST_UNIT_RUN(tc_af_unix_rights_truncation);
|
||||
UTEST_UNIT_RUN(tc_af_unix_rights_peek_and_invalid);
|
||||
UTEST_UNIT_RUN(tc_af_unix_rights_reject_socket);
|
||||
}
|
||||
|
||||
UTEST_TC_EXPORT(tc_af_unix_rights_testcase, "components.dfs.v2.af_unix.rights",
|
||||
RT_NULL, RT_NULL, 15);
|
||||
@@ -108,6 +108,33 @@ struct musl_sockaddr
|
||||
char sa_data[14];
|
||||
};
|
||||
|
||||
struct musl_msghdr
|
||||
{
|
||||
void *msg_name;
|
||||
socklen_t msg_namelen;
|
||||
struct iovec *msg_iov;
|
||||
#if defined(ARCH_CPU_64BIT) && defined(__BYTE_ORDER__) && \
|
||||
__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
int padding1;
|
||||
#endif
|
||||
int msg_iovlen;
|
||||
#if defined(ARCH_CPU_64BIT) && defined(__BYTE_ORDER__) && \
|
||||
__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
int padding1;
|
||||
#endif
|
||||
void *msg_control;
|
||||
#if defined(ARCH_CPU_64BIT) && defined(__BYTE_ORDER__) && \
|
||||
__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
int padding2;
|
||||
#endif
|
||||
socklen_t msg_controllen;
|
||||
#if defined(ARCH_CPU_64BIT) && defined(__BYTE_ORDER__) && \
|
||||
__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
int padding2;
|
||||
#endif
|
||||
int msg_flags;
|
||||
};
|
||||
|
||||
struct musl_ifmap {
|
||||
unsigned long int mem_start;
|
||||
unsigned long int mem_end;
|
||||
|
||||
+377
-154
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
||||
menu "Network"
|
||||
|
||||
rsource "sal/Kconfig"
|
||||
rsource "af_unix/Kconfig"
|
||||
rsource "netdev/Kconfig"
|
||||
rsource "lwip/Kconfig"
|
||||
rsource "at/Kconfig"
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
menuconfig RT_USING_AF_UNIX
|
||||
bool "AF_UNIX local sockets"
|
||||
depends on RT_USING_SAL
|
||||
depends on SAL_USING_POSIX
|
||||
depends on RT_USING_DFS_V2
|
||||
default n
|
||||
help
|
||||
Enable pathname-based AF_UNIX SOCK_DGRAM and SOCK_STREAM sockets.
|
||||
|
||||
if RT_USING_AF_UNIX
|
||||
|
||||
config AF_UNIX_DGRAM_MAX_SIZE
|
||||
int "Maximum datagram size"
|
||||
range 128 65535
|
||||
default 4096
|
||||
|
||||
config AF_UNIX_DGRAM_QUEUE_LEN
|
||||
int "Datagram receive queue length"
|
||||
range 1 256
|
||||
default 16
|
||||
|
||||
config AF_UNIX_STREAM_BUFFER_SIZE
|
||||
int "Stream receive buffer size"
|
||||
range 256 65535
|
||||
default 4096
|
||||
|
||||
config AF_UNIX_LISTEN_BACKLOG_MAX
|
||||
int "Maximum stream listen backlog"
|
||||
range 1 128
|
||||
default 16
|
||||
|
||||
config AF_UNIX_RIGHTS_MAX
|
||||
int "Maximum file descriptors per SCM_RIGHTS message"
|
||||
range 1 253
|
||||
default 16
|
||||
|
||||
config RT_AF_UNIX_USING_TESTCASES
|
||||
bool "Build AF_UNIX test cases"
|
||||
depends on RT_USING_UTESTCASES
|
||||
default n
|
||||
|
||||
endif
|
||||
@@ -0,0 +1,57 @@
|
||||
# AF_UNIX local sockets
|
||||
|
||||
The AF_UNIX component provides pathname-based local IPC through the existing
|
||||
SAL and POSIX socket APIs. It supports `SOCK_DGRAM`, `SOCK_STREAM`, and
|
||||
`socketpair()` for both socket types.
|
||||
|
||||
## Configuration
|
||||
|
||||
Enable `RT_USING_AF_UNIX`. The component requires `RT_USING_SAL`,
|
||||
`SAL_USING_POSIX`, and `RT_USING_DFS_V2`.
|
||||
|
||||
- `AF_UNIX_DGRAM_MAX_SIZE` bounds one datagram.
|
||||
- `AF_UNIX_DGRAM_QUEUE_LEN` bounds queued datagrams per socket.
|
||||
- `AF_UNIX_STREAM_BUFFER_SIZE` bounds each stream receive buffer.
|
||||
- `AF_UNIX_LISTEN_BACKLOG_MAX` caps the stream accept queue.
|
||||
- `AF_UNIX_RIGHTS_MAX` bounds the file descriptors in one `SCM_RIGHTS` send.
|
||||
- `RT_AF_UNIX_USING_TESTCASES` builds the component utest suite.
|
||||
|
||||
## Pathname behavior
|
||||
|
||||
`bind()` creates an `S_IFSOCK` node through DFSv2. The mounted filesystem must
|
||||
support special nodes through `create_vnode()`; tmpfs and devtmpfs support
|
||||
socket nodes directly. Closing a bound socket leaves its pathname in the
|
||||
filesystem. Applications should call `unlink()` before rebinding, which
|
||||
matches common Unix daemon behavior.
|
||||
|
||||
Removing a pathname prevents new lookups. Existing stream connections and
|
||||
connected datagram endpoints continue to reference their established peers.
|
||||
|
||||
## Descriptor passing
|
||||
|
||||
`sendmsg()` and `recvmsg()` support one or more file descriptors in
|
||||
`SOL_SOCKET`/`SCM_RIGHTS` control messages. The queued reference remains valid
|
||||
after the sender closes its descriptor. On receive, each reference is installed
|
||||
as a new descriptor in the receiving process and retains the same open file
|
||||
description, including its shared file position. Passing an AF_UNIX socket
|
||||
descriptor is not supported and returns `EOPNOTSUPP`.
|
||||
|
||||
For datagram sockets, the control message is atomic with its datagram. For
|
||||
stream sockets, it is associated with the first byte written by `sendmsg()` and
|
||||
is delivered when a receive consumes that byte. A receive without a control
|
||||
buffer discards associated descriptors and reports `MSG_CTRUNC`; `MSG_PEEK`
|
||||
does not install or consume descriptors. At least one payload byte is required
|
||||
when sending `SCM_RIGHTS`.
|
||||
|
||||
Only `SCM_RIGHTS` ancillary data is supported. Credentials and other control
|
||||
message types return `EOPNOTSUPP`.
|
||||
|
||||
## Supported operations
|
||||
|
||||
The component implements bind, connect, listen, accept, send/receive,
|
||||
sendto/recvfrom, sendmsg/recvmsg with descriptor passing, shutdown, socket
|
||||
options, nonblocking I/O, timeouts, poll/select readiness, address queries, and
|
||||
socketpair.
|
||||
|
||||
Linux abstract namespace addresses and credential ancillary data are not
|
||||
supported. The component requires DFSv2.
|
||||
@@ -0,0 +1,12 @@
|
||||
from building import *
|
||||
|
||||
cwd = GetCurrentDir()
|
||||
src = Glob('src/*.c')
|
||||
CPPPATH = [cwd + '/include', cwd + '/src']
|
||||
|
||||
if GetDepend('RT_AF_UNIX_USING_TESTCASES'):
|
||||
src += Glob('testcases/*.c')
|
||||
|
||||
group = DefineGroup('AF_UNIX', src, depend=['RT_USING_AF_UNIX'], CPPPATH=CPPPATH)
|
||||
|
||||
Return('group')
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2026, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef AF_UNIX_H__
|
||||
#define AF_UNIX_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
int af_unix_init(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* AF_UNIX_H__ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2026, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "af_unix_internal.h"
|
||||
|
||||
int af_unix_dgram_connect(struct af_unix_socket *sock,
|
||||
const struct sockaddr *address, socklen_t length)
|
||||
{
|
||||
int result = 0;
|
||||
struct sockaddr_un peer_address;
|
||||
socklen_t peer_length;
|
||||
struct af_unix_socket *peer;
|
||||
|
||||
peer = af_unix_namespace_lookup(address, length, SOCK_DGRAM,
|
||||
&peer_address, &peer_length);
|
||||
if (peer == RT_NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
af_unix_lock();
|
||||
if (sock->closed)
|
||||
{
|
||||
result = af_unix_error(EBADF);
|
||||
}
|
||||
else if (sock->write_shutdown)
|
||||
{
|
||||
result = af_unix_error(EPIPE);
|
||||
}
|
||||
else
|
||||
{
|
||||
af_unix_set_peer_locked(sock, peer);
|
||||
sock->peer_address = peer_address;
|
||||
sock->peer_length = peer_length;
|
||||
sock->connected = 1;
|
||||
}
|
||||
af_unix_unlock();
|
||||
af_unix_socket_put(peer);
|
||||
return result;
|
||||
}
|
||||
|
||||
static struct af_unix_socket *af_unix_dgram_target_get(
|
||||
struct af_unix_socket *sock, const struct sockaddr *to,
|
||||
socklen_t to_length)
|
||||
{
|
||||
struct af_unix_socket *peer = RT_NULL;
|
||||
|
||||
if (to != RT_NULL)
|
||||
{
|
||||
return af_unix_namespace_lookup(to, to_length, SOCK_DGRAM,
|
||||
RT_NULL, RT_NULL);
|
||||
}
|
||||
|
||||
af_unix_lock();
|
||||
if (sock->connected && sock->peer != RT_NULL)
|
||||
{
|
||||
peer = sock->peer;
|
||||
af_unix_socket_ref_locked(peer);
|
||||
}
|
||||
af_unix_unlock();
|
||||
|
||||
if (peer == RT_NULL)
|
||||
{
|
||||
af_unix_error(EDESTADDRREQ);
|
||||
}
|
||||
return peer;
|
||||
}
|
||||
|
||||
int af_unix_dgram_send(struct af_unix_socket *sock, const void *data,
|
||||
size_t size, int flags, const struct sockaddr *to,
|
||||
socklen_t to_length, struct af_unix_rights *rights)
|
||||
{
|
||||
int error;
|
||||
int result;
|
||||
int nonblocking;
|
||||
int timeout;
|
||||
struct af_unix_socket *peer;
|
||||
struct af_unix_message *message;
|
||||
|
||||
if (size > AF_UNIX_DGRAM_MAX_SIZE)
|
||||
{
|
||||
return af_unix_error(EMSGSIZE);
|
||||
}
|
||||
if (size != 0 && data == RT_NULL)
|
||||
{
|
||||
return af_unix_error(EINVAL);
|
||||
}
|
||||
|
||||
peer = af_unix_dgram_target_get(sock, to, to_length);
|
||||
if (peer == RT_NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
message = (struct af_unix_message *)rt_malloc(sizeof(*message) + size);
|
||||
if (message == RT_NULL)
|
||||
{
|
||||
af_unix_socket_put(peer);
|
||||
return af_unix_error(ENOMEM);
|
||||
}
|
||||
rt_list_init(&message->node);
|
||||
message->length = size;
|
||||
message->rights = rights;
|
||||
if (size != 0)
|
||||
{
|
||||
rt_memcpy(message->data, data, size);
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
af_unix_lock();
|
||||
if (sock->closed)
|
||||
{
|
||||
result = af_unix_error(EBADF);
|
||||
af_unix_unlock();
|
||||
break;
|
||||
}
|
||||
if (sock->write_shutdown)
|
||||
{
|
||||
result = af_unix_error(EPIPE);
|
||||
af_unix_unlock();
|
||||
break;
|
||||
}
|
||||
if (peer->closed)
|
||||
{
|
||||
result = af_unix_error(ECONNREFUSED);
|
||||
af_unix_unlock();
|
||||
break;
|
||||
}
|
||||
if (peer->read_shutdown)
|
||||
{
|
||||
result = af_unix_error(EPIPE);
|
||||
af_unix_unlock();
|
||||
break;
|
||||
}
|
||||
if (peer->message_count < AF_UNIX_DGRAM_QUEUE_LEN)
|
||||
{
|
||||
message->source = sock->local_address;
|
||||
message->source_length = sock->local_length;
|
||||
rt_list_insert_before(&peer->message_queue, &message->node);
|
||||
peer->message_count++;
|
||||
rt_wqueue_wakeup_all(&peer->wait_queue,
|
||||
(void *)(rt_ubase_t)POLLIN);
|
||||
af_unix_unlock();
|
||||
af_unix_socket_put(peer);
|
||||
return (int)size;
|
||||
}
|
||||
|
||||
nonblocking = af_unix_is_nonblocking(sock, flags);
|
||||
timeout = sock->send_timeout;
|
||||
af_unix_unlock();
|
||||
if (nonblocking)
|
||||
{
|
||||
result = af_unix_error(EAGAIN);
|
||||
break;
|
||||
}
|
||||
if (af_unix_wait(&peer->wait_queue, timeout) < 0)
|
||||
{
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
error = rt_get_errno();
|
||||
rt_free(message);
|
||||
af_unix_socket_put(peer);
|
||||
rt_set_errno(error);
|
||||
return result;
|
||||
}
|
||||
|
||||
int af_unix_dgram_receive(struct af_unix_socket *sock, void *data,
|
||||
size_t size, int flags, struct sockaddr *from,
|
||||
socklen_t *from_length, rt_list_t *rights)
|
||||
{
|
||||
int result;
|
||||
int nonblocking;
|
||||
int timeout;
|
||||
size_t copy_length;
|
||||
struct af_unix_message *message;
|
||||
|
||||
if (size != 0 && data == RT_NULL)
|
||||
{
|
||||
return af_unix_error(EINVAL);
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
af_unix_lock();
|
||||
if (sock->closed)
|
||||
{
|
||||
af_unix_unlock();
|
||||
return af_unix_error(EBADF);
|
||||
}
|
||||
if (!rt_list_isempty(&sock->message_queue))
|
||||
{
|
||||
message = rt_list_entry(sock->message_queue.next,
|
||||
struct af_unix_message, node);
|
||||
copy_length = message->length;
|
||||
if (copy_length > size)
|
||||
{
|
||||
copy_length = size;
|
||||
}
|
||||
if (copy_length != 0)
|
||||
{
|
||||
rt_memcpy(data, message->data, copy_length);
|
||||
}
|
||||
result = af_unix_address_copy(from, from_length,
|
||||
&message->source,
|
||||
message->source_length);
|
||||
if (result < 0)
|
||||
{
|
||||
af_unix_unlock();
|
||||
return result;
|
||||
}
|
||||
if ((flags & MSG_PEEK) == 0)
|
||||
{
|
||||
rt_list_remove(&message->node);
|
||||
sock->message_count--;
|
||||
if (message->rights != RT_NULL)
|
||||
{
|
||||
rt_list_insert_before(rights, &message->rights->node);
|
||||
message->rights = RT_NULL;
|
||||
}
|
||||
af_unix_wakeup_writable_locked(sock);
|
||||
rt_free(message);
|
||||
}
|
||||
af_unix_unlock();
|
||||
return (int)copy_length;
|
||||
}
|
||||
if (sock->read_shutdown)
|
||||
{
|
||||
af_unix_unlock();
|
||||
return 0;
|
||||
}
|
||||
|
||||
nonblocking = af_unix_is_nonblocking(sock, flags);
|
||||
timeout = sock->receive_timeout;
|
||||
af_unix_unlock();
|
||||
if (nonblocking)
|
||||
{
|
||||
return af_unix_error(EAGAIN);
|
||||
}
|
||||
if (af_unix_wait(&sock->wait_queue, timeout) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2026, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef AF_UNIX_INTERNAL_H__
|
||||
#define AF_UNIX_INTERNAL_H__
|
||||
|
||||
#include <rtthread.h>
|
||||
#include <dfs.h>
|
||||
#include <dfs_file.h>
|
||||
#include <poll.h>
|
||||
#include <sal_low_lvl.h>
|
||||
#include <sal_socket.h>
|
||||
|
||||
#ifndef AF_UNIX_DGRAM_MAX_SIZE
|
||||
#define AF_UNIX_DGRAM_MAX_SIZE 4096
|
||||
#endif
|
||||
|
||||
#ifndef AF_UNIX_DGRAM_QUEUE_LEN
|
||||
#define AF_UNIX_DGRAM_QUEUE_LEN 16
|
||||
#endif
|
||||
|
||||
#ifndef AF_UNIX_STREAM_BUFFER_SIZE
|
||||
#define AF_UNIX_STREAM_BUFFER_SIZE 4096
|
||||
#endif
|
||||
|
||||
#ifndef AF_UNIX_LISTEN_BACKLOG_MAX
|
||||
#define AF_UNIX_LISTEN_BACKLOG_MAX 16
|
||||
#endif
|
||||
|
||||
#ifndef AF_UNIX_RIGHTS_MAX
|
||||
#define AF_UNIX_RIGHTS_MAX 16
|
||||
#endif
|
||||
|
||||
#define AF_UNIX_PATH_MAX ((int)sizeof(((struct sockaddr_un *)0)->sun_path))
|
||||
|
||||
struct af_unix_rights
|
||||
{
|
||||
rt_list_t node;
|
||||
rt_uint64_t offset;
|
||||
size_t count;
|
||||
struct dfs_file *files[1];
|
||||
};
|
||||
|
||||
struct af_unix_message
|
||||
{
|
||||
rt_list_t node;
|
||||
size_t length;
|
||||
struct sockaddr_un source;
|
||||
socklen_t source_length;
|
||||
struct af_unix_rights *rights;
|
||||
char data[1];
|
||||
};
|
||||
|
||||
struct af_unix_socket
|
||||
{
|
||||
int handle;
|
||||
int type;
|
||||
int flags;
|
||||
int ref_count;
|
||||
int closed;
|
||||
int bound;
|
||||
int connected;
|
||||
int listening;
|
||||
int read_shutdown;
|
||||
int write_shutdown;
|
||||
int socket_error;
|
||||
int receive_timeout;
|
||||
int send_timeout;
|
||||
|
||||
struct sockaddr_un local_address;
|
||||
socklen_t local_length;
|
||||
struct sockaddr_un peer_address;
|
||||
socklen_t peer_length;
|
||||
struct af_unix_socket *peer;
|
||||
void *namespace_entry;
|
||||
|
||||
rt_wqueue_t wait_queue;
|
||||
rt_list_t message_queue;
|
||||
rt_size_t message_count;
|
||||
|
||||
char *stream_buffer;
|
||||
rt_size_t stream_head;
|
||||
rt_size_t stream_length;
|
||||
rt_uint64_t stream_read_offset;
|
||||
rt_uint64_t stream_write_offset;
|
||||
rt_list_t rights_queue;
|
||||
|
||||
rt_list_t pending_queue;
|
||||
rt_list_t pending_node;
|
||||
int pending;
|
||||
int backlog;
|
||||
int pending_count;
|
||||
};
|
||||
|
||||
void af_unix_lock(void);
|
||||
void af_unix_unlock(void);
|
||||
int af_unix_error(int error);
|
||||
int af_unix_is_nonblocking(const struct af_unix_socket *sock, int flags);
|
||||
int af_unix_wait(rt_wqueue_t *queue, int timeout);
|
||||
|
||||
int af_unix_rights_create(const struct msghdr *message,
|
||||
struct af_unix_rights **rights);
|
||||
void af_unix_rights_release(struct af_unix_rights *rights);
|
||||
void af_unix_rights_list_release(rt_list_t *list);
|
||||
void af_unix_rights_defer_locked(struct af_unix_rights *rights);
|
||||
void af_unix_rights_list_defer_locked(rt_list_t *list);
|
||||
void af_unix_rights_drain(void);
|
||||
int af_unix_rights_deliver(rt_list_t *list, struct msghdr *message);
|
||||
int af_unix_rights_init(void);
|
||||
|
||||
struct af_unix_socket *af_unix_socket_create_locked(int type);
|
||||
struct af_unix_socket *af_unix_socket_get(int handle);
|
||||
void af_unix_socket_ref_locked(struct af_unix_socket *sock);
|
||||
void af_unix_socket_unref_locked(struct af_unix_socket *sock);
|
||||
void af_unix_socket_put(struct af_unix_socket *sock);
|
||||
int af_unix_handle_alloc_locked(struct af_unix_socket *sock);
|
||||
void af_unix_handle_remove_locked(struct af_unix_socket *sock);
|
||||
void af_unix_socket_close_locked(struct af_unix_socket *sock);
|
||||
void af_unix_wakeup_writable_locked(struct af_unix_socket *sock);
|
||||
int af_unix_connect_peers_locked(struct af_unix_socket *first,
|
||||
struct af_unix_socket *second);
|
||||
void af_unix_set_peer_locked(struct af_unix_socket *sock,
|
||||
struct af_unix_socket *peer);
|
||||
|
||||
int af_unix_address_parse(const struct sockaddr *address, socklen_t length,
|
||||
char path[AF_UNIX_PATH_MAX]);
|
||||
void af_unix_address_set(struct sockaddr_un *address, socklen_t *length,
|
||||
const char *path);
|
||||
int af_unix_address_copy(struct sockaddr *address, socklen_t *length,
|
||||
const struct sockaddr_un *source,
|
||||
socklen_t source_length);
|
||||
int af_unix_namespace_bind(struct af_unix_socket *sock,
|
||||
const struct sockaddr *address, socklen_t length);
|
||||
struct af_unix_socket *af_unix_namespace_lookup(
|
||||
const struct sockaddr *address, socklen_t length, int type,
|
||||
struct sockaddr_un *normalized_address, socklen_t *normalized_length);
|
||||
void af_unix_namespace_detach_locked(struct af_unix_socket *sock);
|
||||
int af_unix_namespace_init(void);
|
||||
|
||||
int af_unix_dgram_connect(struct af_unix_socket *sock,
|
||||
const struct sockaddr *address, socklen_t length);
|
||||
int af_unix_dgram_send(struct af_unix_socket *sock, const void *data,
|
||||
size_t size, int flags, const struct sockaddr *to,
|
||||
socklen_t to_length, struct af_unix_rights *rights);
|
||||
int af_unix_dgram_receive(struct af_unix_socket *sock, void *data,
|
||||
size_t size, int flags, struct sockaddr *from,
|
||||
socklen_t *from_length, rt_list_t *rights);
|
||||
|
||||
int af_unix_stream_listen(struct af_unix_socket *sock, int backlog);
|
||||
int af_unix_stream_connect(struct af_unix_socket *sock,
|
||||
const struct sockaddr *address, socklen_t length);
|
||||
int af_unix_stream_accept(struct af_unix_socket *sock,
|
||||
struct sockaddr *address, socklen_t *length);
|
||||
int af_unix_stream_send(struct af_unix_socket *sock, const void *data,
|
||||
size_t size, int flags,
|
||||
struct af_unix_rights *rights);
|
||||
int af_unix_stream_receive(struct af_unix_socket *sock, void *data,
|
||||
size_t size, int flags, rt_list_t *rights);
|
||||
int af_unix_stream_shutdown(struct af_unix_socket *sock, int how);
|
||||
|
||||
#endif /* AF_UNIX_INTERNAL_H__ */
|
||||
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2026, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "af_unix_internal.h"
|
||||
|
||||
struct af_unix_namespace_entry
|
||||
{
|
||||
rt_list_t node;
|
||||
char path[AF_UNIX_PATH_MAX];
|
||||
struct af_unix_socket *sock;
|
||||
};
|
||||
|
||||
static rt_list_t af_unix_namespace;
|
||||
|
||||
static struct af_unix_namespace_entry *af_unix_namespace_find_locked(
|
||||
const char *path)
|
||||
{
|
||||
struct af_unix_namespace_entry *entry;
|
||||
|
||||
rt_list_for_each_entry(entry, &af_unix_namespace, node)
|
||||
{
|
||||
if (rt_strcmp(entry->path, path) == 0)
|
||||
{
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return RT_NULL;
|
||||
}
|
||||
|
||||
int af_unix_address_parse(const struct sockaddr *address, socklen_t length,
|
||||
char path[AF_UNIX_PATH_MAX])
|
||||
{
|
||||
int index;
|
||||
int path_length;
|
||||
char input_path[AF_UNIX_PATH_MAX];
|
||||
char *normalized;
|
||||
const struct sockaddr_un *unix_address;
|
||||
const int path_offset = (int)offsetof(struct sockaddr_un, sun_path);
|
||||
|
||||
if (address == RT_NULL || length <= (socklen_t)path_offset ||
|
||||
length > sizeof(struct sockaddr_un))
|
||||
{
|
||||
return af_unix_error(EINVAL);
|
||||
}
|
||||
|
||||
unix_address = (const struct sockaddr_un *)address;
|
||||
if (unix_address->sa_family != AF_UNIX)
|
||||
{
|
||||
return af_unix_error(EAFNOSUPPORT);
|
||||
}
|
||||
|
||||
path_length = (int)length - path_offset;
|
||||
if (path_length > AF_UNIX_PATH_MAX)
|
||||
{
|
||||
path_length = AF_UNIX_PATH_MAX;
|
||||
}
|
||||
for (index = 0; index < path_length; index++)
|
||||
{
|
||||
if (unix_address->sun_path[index] == '\0')
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index == 0)
|
||||
{
|
||||
return af_unix_error(EINVAL);
|
||||
}
|
||||
if (index == path_length || index >= AF_UNIX_PATH_MAX)
|
||||
{
|
||||
return af_unix_error(ENAMETOOLONG);
|
||||
}
|
||||
|
||||
rt_memcpy(input_path, unix_address->sun_path, index);
|
||||
input_path[index] = '\0';
|
||||
normalized = dfs_normalize_path(RT_NULL, input_path);
|
||||
if (normalized == RT_NULL)
|
||||
{
|
||||
return af_unix_error(ENOMEM);
|
||||
}
|
||||
if (rt_strlen(normalized) >= AF_UNIX_PATH_MAX)
|
||||
{
|
||||
rt_free(normalized);
|
||||
return af_unix_error(ENAMETOOLONG);
|
||||
}
|
||||
rt_strncpy(path, normalized, AF_UNIX_PATH_MAX);
|
||||
path[AF_UNIX_PATH_MAX - 1] = '\0';
|
||||
rt_free(normalized);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void af_unix_address_set(struct sockaddr_un *address, socklen_t *length,
|
||||
const char *path)
|
||||
{
|
||||
size_t path_length = 0;
|
||||
|
||||
rt_memset(address, 0, sizeof(*address));
|
||||
address->sa_family = AF_UNIX;
|
||||
if (path != RT_NULL)
|
||||
{
|
||||
path_length = rt_strlen(path);
|
||||
if (path_length >= sizeof(address->sun_path))
|
||||
{
|
||||
path_length = sizeof(address->sun_path) - 1;
|
||||
}
|
||||
rt_memcpy(address->sun_path, path, path_length);
|
||||
}
|
||||
*length = (socklen_t)(offsetof(struct sockaddr_un, sun_path) +
|
||||
path_length + (path != RT_NULL ? 1 : 0));
|
||||
}
|
||||
|
||||
int af_unix_address_copy(struct sockaddr *address, socklen_t *length,
|
||||
const struct sockaddr_un *source,
|
||||
socklen_t source_length)
|
||||
{
|
||||
socklen_t copy_length;
|
||||
|
||||
if (address == RT_NULL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (length == RT_NULL)
|
||||
{
|
||||
return af_unix_error(EINVAL);
|
||||
}
|
||||
|
||||
copy_length = *length;
|
||||
if (copy_length > source_length)
|
||||
{
|
||||
copy_length = source_length;
|
||||
}
|
||||
if (copy_length > 0)
|
||||
{
|
||||
rt_memcpy(address, source, copy_length);
|
||||
}
|
||||
*length = source_length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int af_unix_namespace_bind(struct af_unix_socket *sock,
|
||||
const struct sockaddr *address, socklen_t length)
|
||||
{
|
||||
int result;
|
||||
char path[AF_UNIX_PATH_MAX];
|
||||
struct af_unix_namespace_entry *entry;
|
||||
struct af_unix_namespace_entry *new_entry;
|
||||
|
||||
result = af_unix_address_parse(address, length, path);
|
||||
if (result < 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
af_unix_lock();
|
||||
if (sock->closed)
|
||||
{
|
||||
af_unix_unlock();
|
||||
return af_unix_error(EBADF);
|
||||
}
|
||||
if (sock->bound)
|
||||
{
|
||||
af_unix_unlock();
|
||||
return af_unix_error(EINVAL);
|
||||
}
|
||||
af_unix_unlock();
|
||||
|
||||
result = dfs_file_mknod(path, FT_SOCKET,
|
||||
S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
if (result < 0)
|
||||
{
|
||||
return af_unix_error(result == -EEXIST ? EADDRINUSE : -result);
|
||||
}
|
||||
|
||||
new_entry = (struct af_unix_namespace_entry *)rt_calloc(1,
|
||||
sizeof(*new_entry));
|
||||
if (new_entry == RT_NULL)
|
||||
{
|
||||
(void)dfs_file_unlink(path);
|
||||
return af_unix_error(ENOMEM);
|
||||
}
|
||||
rt_strncpy(new_entry->path, path, sizeof(new_entry->path));
|
||||
new_entry->path[sizeof(new_entry->path) - 1] = '\0';
|
||||
rt_list_init(&new_entry->node);
|
||||
|
||||
af_unix_lock();
|
||||
if (sock->closed || sock->bound)
|
||||
{
|
||||
af_unix_unlock();
|
||||
rt_free(new_entry);
|
||||
(void)dfs_file_unlink(path);
|
||||
return af_unix_error(sock->closed ? EBADF : EINVAL);
|
||||
}
|
||||
|
||||
entry = af_unix_namespace_find_locked(path);
|
||||
if (entry == RT_NULL)
|
||||
{
|
||||
entry = new_entry;
|
||||
new_entry = RT_NULL;
|
||||
rt_list_insert_before(&af_unix_namespace, &entry->node);
|
||||
}
|
||||
entry->sock = sock;
|
||||
sock->namespace_entry = entry;
|
||||
sock->bound = 1;
|
||||
af_unix_address_set(&sock->local_address, &sock->local_length, path);
|
||||
af_unix_unlock();
|
||||
|
||||
if (new_entry != RT_NULL)
|
||||
{
|
||||
rt_free(new_entry);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct af_unix_socket *af_unix_namespace_lookup(
|
||||
const struct sockaddr *address, socklen_t length, int type,
|
||||
struct sockaddr_un *normalized_address, socklen_t *normalized_length)
|
||||
{
|
||||
int result;
|
||||
char path[AF_UNIX_PATH_MAX];
|
||||
struct stat file_stat;
|
||||
struct af_unix_socket *sock = RT_NULL;
|
||||
struct af_unix_namespace_entry *entry;
|
||||
|
||||
result = af_unix_address_parse(address, length, path);
|
||||
if (result < 0)
|
||||
{
|
||||
return RT_NULL;
|
||||
}
|
||||
result = dfs_file_stat(path, &file_stat);
|
||||
if (result < 0)
|
||||
{
|
||||
af_unix_error(ENOENT);
|
||||
return RT_NULL;
|
||||
}
|
||||
if (!S_ISSOCK(file_stat.st_mode))
|
||||
{
|
||||
af_unix_error(EPROTOTYPE);
|
||||
return RT_NULL;
|
||||
}
|
||||
|
||||
af_unix_lock();
|
||||
entry = af_unix_namespace_find_locked(path);
|
||||
if (entry != RT_NULL && entry->sock != RT_NULL && !entry->sock->closed)
|
||||
{
|
||||
if (entry->sock->type == type)
|
||||
{
|
||||
sock = entry->sock;
|
||||
af_unix_socket_ref_locked(sock);
|
||||
}
|
||||
else
|
||||
{
|
||||
af_unix_error(EPROTOTYPE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
af_unix_error(ECONNREFUSED);
|
||||
}
|
||||
af_unix_unlock();
|
||||
|
||||
if (sock != RT_NULL && normalized_address != RT_NULL &&
|
||||
normalized_length != RT_NULL)
|
||||
{
|
||||
af_unix_address_set(normalized_address, normalized_length, path);
|
||||
}
|
||||
return sock;
|
||||
}
|
||||
|
||||
void af_unix_namespace_detach_locked(struct af_unix_socket *sock)
|
||||
{
|
||||
struct af_unix_namespace_entry *entry;
|
||||
|
||||
entry = (struct af_unix_namespace_entry *)sock->namespace_entry;
|
||||
if (entry != RT_NULL && entry->sock == sock)
|
||||
{
|
||||
entry->sock = RT_NULL;
|
||||
rt_list_remove(&entry->node);
|
||||
rt_free(entry);
|
||||
}
|
||||
sock->namespace_entry = RT_NULL;
|
||||
}
|
||||
|
||||
int af_unix_namespace_init(void)
|
||||
{
|
||||
rt_list_init(&af_unix_namespace);
|
||||
return RT_EOK;
|
||||
}
|
||||
@@ -0,0 +1,364 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2026, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <dfs_net.h>
|
||||
|
||||
#include "af_unix_internal.h"
|
||||
|
||||
static struct rt_mutex af_unix_deferred_lock;
|
||||
static rt_list_t af_unix_deferred_rights;
|
||||
|
||||
static size_t af_unix_cmsg_next(size_t offset, size_t length,
|
||||
size_t control_length)
|
||||
{
|
||||
size_t next;
|
||||
|
||||
next = offset + CMSG_ALIGN(length);
|
||||
if (next > control_length)
|
||||
{
|
||||
next = offset + length;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
static int af_unix_file_is_unix_socket(const struct dfs_file *file)
|
||||
{
|
||||
int sal_handle;
|
||||
struct sal_socket *sal_sock;
|
||||
|
||||
if (file == RT_NULL || file->vnode == RT_NULL ||
|
||||
file->vnode->type != FT_SOCKET)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (file->vnode->fops != dfs_net_get_fops() &&
|
||||
file->fops != dfs_net_get_fops())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
sal_handle = (int)(size_t)file->vnode->data;
|
||||
sal_sock = sal_get_socket(sal_handle);
|
||||
return (sal_sock != RT_NULL && sal_sock->domain == AF_UNIX);
|
||||
}
|
||||
|
||||
int af_unix_rights_create(const struct msghdr *message,
|
||||
struct af_unix_rights **out_rights)
|
||||
{
|
||||
int fds[AF_UNIX_RIGHTS_MAX];
|
||||
int result;
|
||||
size_t count = 0;
|
||||
size_t file_index;
|
||||
size_t offset = 0;
|
||||
const char *control;
|
||||
struct af_unix_rights *rights;
|
||||
|
||||
*out_rights = RT_NULL;
|
||||
if (message->msg_controllen == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (message->msg_control == RT_NULL)
|
||||
{
|
||||
return af_unix_error(EINVAL);
|
||||
}
|
||||
|
||||
control = (const char *)message->msg_control;
|
||||
while (offset < message->msg_controllen)
|
||||
{
|
||||
size_t data_length;
|
||||
size_t fd_count;
|
||||
size_t index;
|
||||
size_t next;
|
||||
const struct cmsghdr *cmsg;
|
||||
|
||||
if (message->msg_controllen - offset < sizeof(*cmsg))
|
||||
{
|
||||
return af_unix_error(EINVAL);
|
||||
}
|
||||
cmsg = (const struct cmsghdr *)(control + offset);
|
||||
if (cmsg->cmsg_len < sizeof(*cmsg) ||
|
||||
cmsg->cmsg_len > message->msg_controllen - offset)
|
||||
{
|
||||
return af_unix_error(EINVAL);
|
||||
}
|
||||
if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS)
|
||||
{
|
||||
return af_unix_error(EOPNOTSUPP);
|
||||
}
|
||||
|
||||
data_length = cmsg->cmsg_len - sizeof(*cmsg);
|
||||
if (data_length == 0 || data_length % sizeof(int) != 0)
|
||||
{
|
||||
return af_unix_error(EINVAL);
|
||||
}
|
||||
fd_count = data_length / sizeof(int);
|
||||
if (fd_count > AF_UNIX_RIGHTS_MAX - count)
|
||||
{
|
||||
return af_unix_error(EMSGSIZE);
|
||||
}
|
||||
for (index = 0; index < fd_count; index++)
|
||||
{
|
||||
rt_memcpy(&fds[count],
|
||||
(const char *)CMSG_DATA(cmsg) + index * sizeof(int),
|
||||
sizeof(int));
|
||||
count++;
|
||||
}
|
||||
|
||||
next = af_unix_cmsg_next(offset, cmsg->cmsg_len,
|
||||
message->msg_controllen);
|
||||
if (next <= offset || next > message->msg_controllen)
|
||||
{
|
||||
return af_unix_error(EINVAL);
|
||||
}
|
||||
offset = next;
|
||||
}
|
||||
|
||||
rights = (struct af_unix_rights *)rt_calloc(
|
||||
1, sizeof(*rights) + (count - 1) * sizeof(rights->files[0]));
|
||||
if (rights == RT_NULL)
|
||||
{
|
||||
return af_unix_error(ENOMEM);
|
||||
}
|
||||
rt_list_init(&rights->node);
|
||||
rights->count = count;
|
||||
|
||||
result = dfs_file_get_refs(fds, count, rights->files);
|
||||
if (result < 0)
|
||||
{
|
||||
rt_free(rights);
|
||||
return af_unix_error(-result);
|
||||
}
|
||||
|
||||
/* Passing AF_UNIX sockets can form an unreclaimable reference
|
||||
* cycle between the in-flight right and the destination queue. */
|
||||
for (file_index = 0; file_index < count; file_index++)
|
||||
{
|
||||
if (af_unix_file_is_unix_socket(rights->files[file_index]))
|
||||
{
|
||||
af_unix_rights_release(rights);
|
||||
return af_unix_error(EOPNOTSUPP);
|
||||
}
|
||||
}
|
||||
|
||||
*out_rights = rights;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void af_unix_rights_release(struct af_unix_rights *rights)
|
||||
{
|
||||
size_t index;
|
||||
|
||||
if (rights == RT_NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
for (index = 0; index < rights->count; index++)
|
||||
{
|
||||
if (rights->files[index] != RT_NULL)
|
||||
{
|
||||
dfs_file_put_ref(rights->files[index]);
|
||||
}
|
||||
}
|
||||
rt_free(rights);
|
||||
}
|
||||
|
||||
void af_unix_rights_list_release(rt_list_t *list)
|
||||
{
|
||||
while (!rt_list_isempty(list))
|
||||
{
|
||||
struct af_unix_rights *rights;
|
||||
|
||||
rights = rt_list_entry(list->next, struct af_unix_rights, node);
|
||||
rt_list_remove(&rights->node);
|
||||
af_unix_rights_release(rights);
|
||||
}
|
||||
}
|
||||
|
||||
void af_unix_rights_defer_locked(struct af_unix_rights *rights)
|
||||
{
|
||||
if (rights == RT_NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
rt_mutex_take(&af_unix_deferred_lock, RT_WAITING_FOREVER);
|
||||
rt_list_insert_before(&af_unix_deferred_rights, &rights->node);
|
||||
rt_mutex_release(&af_unix_deferred_lock);
|
||||
}
|
||||
|
||||
void af_unix_rights_list_defer_locked(rt_list_t *list)
|
||||
{
|
||||
while (!rt_list_isempty(list))
|
||||
{
|
||||
struct af_unix_rights *rights;
|
||||
|
||||
rights = rt_list_entry(list->next, struct af_unix_rights, node);
|
||||
rt_list_remove(&rights->node);
|
||||
af_unix_rights_defer_locked(rights);
|
||||
}
|
||||
}
|
||||
|
||||
void af_unix_rights_drain(void)
|
||||
{
|
||||
rt_list_t pending;
|
||||
|
||||
rt_list_init(&pending);
|
||||
rt_mutex_take(&af_unix_deferred_lock, RT_WAITING_FOREVER);
|
||||
while (!rt_list_isempty(&af_unix_deferred_rights))
|
||||
{
|
||||
rt_list_t *node = af_unix_deferred_rights.next;
|
||||
|
||||
rt_list_remove(node);
|
||||
rt_list_insert_before(&pending, node);
|
||||
}
|
||||
rt_mutex_release(&af_unix_deferred_lock);
|
||||
|
||||
af_unix_rights_list_release(&pending);
|
||||
}
|
||||
|
||||
static size_t af_unix_rights_count(const rt_list_t *list)
|
||||
{
|
||||
size_t count = 0;
|
||||
const rt_list_t *node;
|
||||
|
||||
for (node = list->next; node != list; node = node->next)
|
||||
{
|
||||
const struct af_unix_rights *rights;
|
||||
|
||||
rights = rt_list_entry(node, struct af_unix_rights, node);
|
||||
count += rights->count;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static size_t af_unix_rights_capacity(const struct msghdr *message,
|
||||
size_t total)
|
||||
{
|
||||
size_t capacity;
|
||||
|
||||
if (message->msg_control == RT_NULL ||
|
||||
message->msg_controllen < CMSG_SPACE(sizeof(int)))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
capacity = (message->msg_controllen - sizeof(struct cmsghdr)) /
|
||||
sizeof(int);
|
||||
if (capacity > total)
|
||||
{
|
||||
capacity = total;
|
||||
}
|
||||
while (capacity != 0 &&
|
||||
CMSG_SPACE(capacity * sizeof(int)) > message->msg_controllen)
|
||||
{
|
||||
capacity--;
|
||||
}
|
||||
return capacity;
|
||||
}
|
||||
|
||||
int af_unix_rights_deliver(rt_list_t *list, struct msghdr *message)
|
||||
{
|
||||
int install_result = 0;
|
||||
int *fds = RT_NULL;
|
||||
size_t capacity;
|
||||
size_t delivered = 0;
|
||||
size_t index;
|
||||
size_t total;
|
||||
struct dfs_file **files = RT_NULL;
|
||||
struct cmsghdr *cmsg;
|
||||
rt_list_t *node;
|
||||
|
||||
total = af_unix_rights_count(list);
|
||||
capacity = af_unix_rights_capacity(message, total);
|
||||
if (capacity != 0)
|
||||
{
|
||||
files = (struct dfs_file **)rt_malloc(capacity * sizeof(*files));
|
||||
fds = (int *)rt_malloc(capacity * sizeof(*fds));
|
||||
if (files == RT_NULL || fds == RT_NULL)
|
||||
{
|
||||
capacity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for (node = list->next; node != list && delivered < capacity;
|
||||
node = node->next)
|
||||
{
|
||||
struct af_unix_rights *rights;
|
||||
|
||||
rights = rt_list_entry(node, struct af_unix_rights, node);
|
||||
for (index = 0; index < rights->count && delivered < capacity; index++)
|
||||
{
|
||||
files[delivered] = rights->files[index];
|
||||
delivered++;
|
||||
}
|
||||
}
|
||||
|
||||
if (delivered != 0)
|
||||
{
|
||||
install_result = dfs_file_install_refs(files, delivered, fds);
|
||||
if (install_result < 0)
|
||||
{
|
||||
delivered = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (delivered != 0)
|
||||
{
|
||||
size_t transferred = 0;
|
||||
|
||||
for (node = list->next; node != list && transferred < delivered;
|
||||
node = node->next)
|
||||
{
|
||||
struct af_unix_rights *rights;
|
||||
|
||||
rights = rt_list_entry(node, struct af_unix_rights, node);
|
||||
for (index = 0;
|
||||
index < rights->count && transferred < delivered; index++)
|
||||
{
|
||||
rights->files[index] = RT_NULL;
|
||||
transferred++;
|
||||
}
|
||||
}
|
||||
|
||||
cmsg = (struct cmsghdr *)message->msg_control;
|
||||
cmsg->cmsg_len = CMSG_LEN(delivered * sizeof(int));
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
rt_memcpy(CMSG_DATA(cmsg), fds, delivered * sizeof(int));
|
||||
message->msg_controllen = CMSG_SPACE(delivered * sizeof(int));
|
||||
}
|
||||
else
|
||||
{
|
||||
message->msg_controllen = 0;
|
||||
}
|
||||
|
||||
if (delivered < total)
|
||||
{
|
||||
message->msg_flags |= MSG_CTRUNC;
|
||||
}
|
||||
|
||||
if (files != RT_NULL)
|
||||
{
|
||||
rt_free(files);
|
||||
}
|
||||
if (fds != RT_NULL)
|
||||
{
|
||||
rt_free(fds);
|
||||
}
|
||||
af_unix_rights_list_release(list);
|
||||
return install_result;
|
||||
}
|
||||
|
||||
int af_unix_rights_init(void)
|
||||
{
|
||||
rt_list_init(&af_unix_deferred_rights);
|
||||
return rt_mutex_init(&af_unix_deferred_lock, "afuright",
|
||||
RT_IPC_FLAG_PRIO);
|
||||
}
|
||||
@@ -0,0 +1,483 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2026, RT-Thread Development Team
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "af_unix_internal.h"
|
||||
|
||||
static void af_unix_stream_buffer_write_locked(struct af_unix_socket *sock,
|
||||
const char *data,
|
||||
size_t length)
|
||||
{
|
||||
size_t first_length;
|
||||
size_t tail;
|
||||
|
||||
tail = (sock->stream_head + sock->stream_length) %
|
||||
AF_UNIX_STREAM_BUFFER_SIZE;
|
||||
first_length = AF_UNIX_STREAM_BUFFER_SIZE - tail;
|
||||
if (first_length > length)
|
||||
{
|
||||
first_length = length;
|
||||
}
|
||||
rt_memcpy(sock->stream_buffer + tail, data, first_length);
|
||||
if (length > first_length)
|
||||
{
|
||||
rt_memcpy(sock->stream_buffer, data + first_length,
|
||||
length - first_length);
|
||||
}
|
||||
sock->stream_length += length;
|
||||
sock->stream_write_offset += length;
|
||||
}
|
||||
|
||||
static void af_unix_stream_buffer_read_locked(struct af_unix_socket *sock,
|
||||
char *data, size_t length,
|
||||
int peek)
|
||||
{
|
||||
size_t first_length;
|
||||
|
||||
first_length = AF_UNIX_STREAM_BUFFER_SIZE - sock->stream_head;
|
||||
if (first_length > length)
|
||||
{
|
||||
first_length = length;
|
||||
}
|
||||
rt_memcpy(data, sock->stream_buffer + sock->stream_head, first_length);
|
||||
if (length > first_length)
|
||||
{
|
||||
rt_memcpy(data + first_length, sock->stream_buffer,
|
||||
length - first_length);
|
||||
}
|
||||
|
||||
if (!peek)
|
||||
{
|
||||
sock->stream_head = (sock->stream_head + length) %
|
||||
AF_UNIX_STREAM_BUFFER_SIZE;
|
||||
sock->stream_length -= length;
|
||||
sock->stream_read_offset += length;
|
||||
}
|
||||
}
|
||||
|
||||
static void af_unix_stream_collect_rights_locked(struct af_unix_socket *sock,
|
||||
rt_list_t *rights)
|
||||
{
|
||||
while (!rt_list_isempty(&sock->rights_queue))
|
||||
{
|
||||
struct af_unix_rights *entry;
|
||||
|
||||
entry = rt_list_entry(sock->rights_queue.next,
|
||||
struct af_unix_rights, node);
|
||||
if (entry->offset >= sock->stream_read_offset)
|
||||
{
|
||||
break;
|
||||
}
|
||||
rt_list_remove(&entry->node);
|
||||
rt_list_insert_before(rights, &entry->node);
|
||||
}
|
||||
}
|
||||
|
||||
int af_unix_stream_listen(struct af_unix_socket *sock, int backlog)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
af_unix_lock();
|
||||
if (sock->closed)
|
||||
{
|
||||
result = af_unix_error(EBADF);
|
||||
}
|
||||
else if (sock->type != SOCK_STREAM)
|
||||
{
|
||||
result = af_unix_error(EOPNOTSUPP);
|
||||
}
|
||||
else if (!sock->bound)
|
||||
{
|
||||
result = af_unix_error(EINVAL);
|
||||
}
|
||||
else if (sock->connected)
|
||||
{
|
||||
result = af_unix_error(EINVAL);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (backlog < 1)
|
||||
{
|
||||
backlog = 1;
|
||||
}
|
||||
if (backlog > AF_UNIX_LISTEN_BACKLOG_MAX)
|
||||
{
|
||||
backlog = AF_UNIX_LISTEN_BACKLOG_MAX;
|
||||
}
|
||||
sock->backlog = backlog;
|
||||
sock->listening = 1;
|
||||
}
|
||||
af_unix_unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
int af_unix_stream_connect(struct af_unix_socket *sock,
|
||||
const struct sockaddr *address, socklen_t length)
|
||||
{
|
||||
int handle;
|
||||
int nonblocking;
|
||||
int timeout;
|
||||
int result = -1;
|
||||
struct sockaddr_un peer_address;
|
||||
socklen_t peer_length;
|
||||
struct af_unix_socket *listener;
|
||||
struct af_unix_socket *accepted;
|
||||
|
||||
listener = af_unix_namespace_lookup(address, length, SOCK_STREAM,
|
||||
&peer_address, &peer_length);
|
||||
if (listener == RT_NULL)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
af_unix_lock();
|
||||
if (sock->closed)
|
||||
{
|
||||
result = af_unix_error(EBADF);
|
||||
af_unix_unlock();
|
||||
break;
|
||||
}
|
||||
if (sock->connected)
|
||||
{
|
||||
result = af_unix_error(EISCONN);
|
||||
af_unix_unlock();
|
||||
break;
|
||||
}
|
||||
if (listener->closed || !listener->listening)
|
||||
{
|
||||
result = af_unix_error(ECONNREFUSED);
|
||||
af_unix_unlock();
|
||||
break;
|
||||
}
|
||||
if (listener->pending_count < listener->backlog)
|
||||
{
|
||||
accepted = af_unix_socket_create_locked(SOCK_STREAM);
|
||||
if (accepted == RT_NULL)
|
||||
{
|
||||
result = af_unix_error(ENOMEM);
|
||||
af_unix_unlock();
|
||||
break;
|
||||
}
|
||||
handle = af_unix_handle_alloc_locked(accepted);
|
||||
if (handle < 0)
|
||||
{
|
||||
af_unix_socket_unref_locked(accepted);
|
||||
result = af_unix_error(EMFILE);
|
||||
af_unix_unlock();
|
||||
break;
|
||||
}
|
||||
|
||||
result = af_unix_connect_peers_locked(sock, accepted);
|
||||
if (result < 0)
|
||||
{
|
||||
af_unix_handle_remove_locked(accepted);
|
||||
af_unix_unlock();
|
||||
break;
|
||||
}
|
||||
|
||||
sock->peer_address = peer_address;
|
||||
sock->peer_length = peer_length;
|
||||
accepted->local_address = listener->local_address;
|
||||
accepted->local_length = listener->local_length;
|
||||
accepted->peer_address = sock->local_address;
|
||||
accepted->peer_length = sock->local_length;
|
||||
accepted->pending = 1;
|
||||
rt_list_insert_before(&listener->pending_queue,
|
||||
&accepted->pending_node);
|
||||
listener->pending_count++;
|
||||
rt_wqueue_wakeup_all(&listener->wait_queue,
|
||||
(void *)(rt_ubase_t)POLLIN);
|
||||
af_unix_unlock();
|
||||
result = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
nonblocking = af_unix_is_nonblocking(sock, 0);
|
||||
timeout = sock->send_timeout;
|
||||
af_unix_unlock();
|
||||
if (nonblocking)
|
||||
{
|
||||
result = af_unix_error(EAGAIN);
|
||||
break;
|
||||
}
|
||||
if (af_unix_wait(&listener->wait_queue, timeout) < 0)
|
||||
{
|
||||
result = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
af_unix_socket_put(listener);
|
||||
return result;
|
||||
}
|
||||
|
||||
int af_unix_stream_accept(struct af_unix_socket *sock,
|
||||
struct sockaddr *address, socklen_t *length)
|
||||
{
|
||||
int handle;
|
||||
int nonblocking;
|
||||
int timeout;
|
||||
int result;
|
||||
struct af_unix_socket *accepted;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
af_unix_lock();
|
||||
if (sock->closed)
|
||||
{
|
||||
af_unix_unlock();
|
||||
return af_unix_error(EBADF);
|
||||
}
|
||||
if (!sock->listening)
|
||||
{
|
||||
af_unix_unlock();
|
||||
return af_unix_error(EINVAL);
|
||||
}
|
||||
if (!rt_list_isempty(&sock->pending_queue))
|
||||
{
|
||||
accepted = rt_list_entry(sock->pending_queue.next,
|
||||
struct af_unix_socket, pending_node);
|
||||
result = af_unix_address_copy(address, length,
|
||||
&accepted->peer_address,
|
||||
accepted->peer_length);
|
||||
if (result < 0)
|
||||
{
|
||||
af_unix_unlock();
|
||||
return result;
|
||||
}
|
||||
rt_list_remove(&accepted->pending_node);
|
||||
accepted->pending = 0;
|
||||
sock->pending_count--;
|
||||
handle = accepted->handle;
|
||||
rt_wqueue_wakeup_all(&sock->wait_queue,
|
||||
(void *)(rt_ubase_t)POLLOUT);
|
||||
af_unix_unlock();
|
||||
return handle;
|
||||
}
|
||||
|
||||
nonblocking = af_unix_is_nonblocking(sock, 0);
|
||||
timeout = sock->receive_timeout;
|
||||
af_unix_unlock();
|
||||
if (nonblocking)
|
||||
{
|
||||
return af_unix_error(EAGAIN);
|
||||
}
|
||||
if (af_unix_wait(&sock->wait_queue, timeout) < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int af_unix_stream_send(struct af_unix_socket *sock, const void *data,
|
||||
size_t size, int flags,
|
||||
struct af_unix_rights *rights)
|
||||
{
|
||||
int nonblocking;
|
||||
int timeout;
|
||||
size_t copy_length;
|
||||
size_t space;
|
||||
struct af_unix_socket *peer;
|
||||
|
||||
if (size != 0 && data == RT_NULL)
|
||||
{
|
||||
return af_unix_error(EINVAL);
|
||||
}
|
||||
if (size == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
af_unix_lock();
|
||||
if (sock->closed)
|
||||
{
|
||||
af_unix_unlock();
|
||||
return af_unix_error(EBADF);
|
||||
}
|
||||
if (sock->write_shutdown)
|
||||
{
|
||||
af_unix_unlock();
|
||||
return af_unix_error(EPIPE);
|
||||
}
|
||||
peer = sock->peer;
|
||||
if (!sock->connected || peer == RT_NULL)
|
||||
{
|
||||
af_unix_unlock();
|
||||
return af_unix_error(ENOTCONN);
|
||||
}
|
||||
if (peer->closed || peer->read_shutdown)
|
||||
{
|
||||
af_unix_unlock();
|
||||
return af_unix_error(EPIPE);
|
||||
}
|
||||
|
||||
space = AF_UNIX_STREAM_BUFFER_SIZE - peer->stream_length;
|
||||
if (space != 0)
|
||||
{
|
||||
copy_length = size;
|
||||
if (copy_length > space)
|
||||
{
|
||||
copy_length = space;
|
||||
}
|
||||
if (rights != RT_NULL)
|
||||
{
|
||||
rights->offset = peer->stream_write_offset;
|
||||
rt_list_insert_before(&peer->rights_queue, &rights->node);
|
||||
}
|
||||
af_unix_stream_buffer_write_locked(peer, (const char *)data,
|
||||
copy_length);
|
||||
rt_wqueue_wakeup_all(&peer->wait_queue,
|
||||
(void *)(rt_ubase_t)POLLIN);
|
||||
af_unix_unlock();
|
||||
return (int)copy_length;
|
||||
}
|
||||
|
||||
nonblocking = af_unix_is_nonblocking(sock, flags);
|
||||
timeout = sock->send_timeout;
|
||||
af_unix_socket_ref_locked(peer);
|
||||
af_unix_unlock();
|
||||
if (nonblocking)
|
||||
{
|
||||
af_unix_socket_put(peer);
|
||||
return af_unix_error(EAGAIN);
|
||||
}
|
||||
if (af_unix_wait(&peer->wait_queue, timeout) < 0)
|
||||
{
|
||||
af_unix_socket_put(peer);
|
||||
return -1;
|
||||
}
|
||||
af_unix_socket_put(peer);
|
||||
}
|
||||
}
|
||||
|
||||
int af_unix_stream_receive(struct af_unix_socket *sock, void *data,
|
||||
size_t size, int flags, rt_list_t *rights)
|
||||
{
|
||||
int nonblocking;
|
||||
int timeout;
|
||||
int wait_all;
|
||||
size_t copy_length;
|
||||
size_t received = 0;
|
||||
struct af_unix_socket *peer;
|
||||
|
||||
if (size != 0 && data == RT_NULL)
|
||||
{
|
||||
return af_unix_error(EINVAL);
|
||||
}
|
||||
if (size == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
wait_all = ((flags & MSG_WAITALL) != 0 && (flags & MSG_PEEK) == 0);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
af_unix_lock();
|
||||
if (sock->closed)
|
||||
{
|
||||
af_unix_unlock();
|
||||
return received != 0 ? (int)received : af_unix_error(EBADF);
|
||||
}
|
||||
if (sock->read_shutdown)
|
||||
{
|
||||
af_unix_unlock();
|
||||
return (int)received;
|
||||
}
|
||||
if (sock->stream_length != 0)
|
||||
{
|
||||
copy_length = size - received;
|
||||
if (copy_length > sock->stream_length)
|
||||
{
|
||||
copy_length = sock->stream_length;
|
||||
}
|
||||
af_unix_stream_buffer_read_locked(sock,
|
||||
(char *)data + received,
|
||||
copy_length,
|
||||
(flags & MSG_PEEK) != 0);
|
||||
received += copy_length;
|
||||
if ((flags & MSG_PEEK) == 0)
|
||||
{
|
||||
af_unix_stream_collect_rights_locked(sock, rights);
|
||||
af_unix_wakeup_writable_locked(sock);
|
||||
}
|
||||
if (!wait_all || received == size)
|
||||
{
|
||||
af_unix_unlock();
|
||||
return (int)received;
|
||||
}
|
||||
}
|
||||
|
||||
peer = sock->peer;
|
||||
if (peer == RT_NULL || peer->closed || peer->write_shutdown)
|
||||
{
|
||||
af_unix_unlock();
|
||||
return (int)received;
|
||||
}
|
||||
|
||||
nonblocking = af_unix_is_nonblocking(sock, flags);
|
||||
timeout = sock->receive_timeout;
|
||||
af_unix_unlock();
|
||||
if (nonblocking)
|
||||
{
|
||||
return received != 0 ? (int)received : af_unix_error(EAGAIN);
|
||||
}
|
||||
if (af_unix_wait(&sock->wait_queue, timeout) < 0)
|
||||
{
|
||||
return received != 0 ? (int)received : -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int af_unix_stream_shutdown(struct af_unix_socket *sock, int how)
|
||||
{
|
||||
int result = 0;
|
||||
struct af_unix_socket *peer;
|
||||
|
||||
if (how < SHUT_RD || how > SHUT_RDWR)
|
||||
{
|
||||
return af_unix_error(EINVAL);
|
||||
}
|
||||
|
||||
af_unix_lock();
|
||||
if (sock->closed)
|
||||
{
|
||||
result = af_unix_error(EBADF);
|
||||
}
|
||||
else if (!sock->connected)
|
||||
{
|
||||
result = af_unix_error(ENOTCONN);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (how == SHUT_RD || how == SHUT_RDWR)
|
||||
{
|
||||
sock->read_shutdown = 1;
|
||||
sock->stream_head = 0;
|
||||
sock->stream_read_offset += sock->stream_length;
|
||||
sock->stream_length = 0;
|
||||
af_unix_rights_list_defer_locked(&sock->rights_queue);
|
||||
}
|
||||
if (how == SHUT_WR || how == SHUT_RDWR)
|
||||
{
|
||||
sock->write_shutdown = 1;
|
||||
}
|
||||
rt_wqueue_wakeup_all(&sock->wait_queue,
|
||||
(void *)(rt_ubase_t)(POLLIN | POLLOUT));
|
||||
peer = sock->peer;
|
||||
if (peer != RT_NULL)
|
||||
{
|
||||
rt_wqueue_wakeup_all(&peer->wait_queue,
|
||||
(void *)(rt_ubase_t)(POLLIN | POLLOUT));
|
||||
}
|
||||
}
|
||||
af_unix_unlock();
|
||||
return result;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -48,6 +48,7 @@ typedef uint32_t socklen_t;
|
||||
struct sockaddr;
|
||||
struct msghdr;
|
||||
struct addrinfo;
|
||||
struct sal_proto_family;
|
||||
struct sal_socket
|
||||
{
|
||||
uint32_t magic; /* SAL socket magic word */
|
||||
@@ -58,6 +59,7 @@ struct sal_socket
|
||||
int protocol;
|
||||
|
||||
struct netdev *netdev; /* SAL network interface device */
|
||||
const struct sal_proto_family *protocol_family; /* selected protocol provider */
|
||||
|
||||
void *user_data; /* user-specific data */
|
||||
#ifdef SAL_USING_TLS
|
||||
@@ -109,6 +111,9 @@ struct sal_proto_family
|
||||
|
||||
/* SAL(Socket Abstraction Layer) initialize */
|
||||
int sal_init(void);
|
||||
/* Register and find protocol providers which do not require a netdev. */
|
||||
int sal_proto_family_register(const struct sal_proto_family *pf);
|
||||
const struct sal_proto_family *sal_proto_family_find(int family);
|
||||
/* Get SAL socket object by socket descriptor */
|
||||
struct sal_socket *sal_get_socket(int sock);
|
||||
|
||||
|
||||
@@ -119,6 +119,9 @@ typedef uint16_t in_port_t;
|
||||
#define MSG_DONTWAIT 0x08 /* Nonblocking i/o for this operation only */
|
||||
#define MSG_MORE 0x10 /* Sender will send more */
|
||||
|
||||
/* Output-only flags returned through struct msghdr. */
|
||||
#define MSG_CTRUNC 0x08 /* Control data was discarded due to truncation */
|
||||
|
||||
#define MSG_ERRQUEUE 0x2000 /* Fetch message from error queue */
|
||||
#define MSG_CONFIRM 0x0800 /* Confirm path validity */
|
||||
|
||||
|
||||
@@ -725,7 +725,11 @@ int closesocket(int s)
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef RT_USING_DFS_V2
|
||||
if (dfs_file_close(d) == 0)
|
||||
#else
|
||||
if (sal_closesocket(socket) == 0)
|
||||
#endif
|
||||
{
|
||||
error = 0;
|
||||
}
|
||||
@@ -771,8 +775,19 @@ RTM_EXPORT(closesocket);
|
||||
int socketpair(int domain, int type, int protocol, int *fds)
|
||||
{
|
||||
rt_err_t ret = 0;
|
||||
int nonblocking = 0;
|
||||
int sock_fds[2];
|
||||
|
||||
if ((type & SOCK_CLOEXEC) != 0)
|
||||
{
|
||||
type &= ~SOCK_CLOEXEC;
|
||||
}
|
||||
if ((type & SOCK_NONBLOCK) != 0)
|
||||
{
|
||||
nonblocking = 1;
|
||||
type &= ~SOCK_NONBLOCK;
|
||||
}
|
||||
|
||||
fds[0] = socket(domain, type, protocol);
|
||||
if (fds[0] < 0)
|
||||
{
|
||||
@@ -799,6 +814,11 @@ int socketpair(int domain, int type, int protocol, int *fds)
|
||||
closesocket(fds[0]);
|
||||
closesocket(fds[1]);
|
||||
}
|
||||
else if (nonblocking)
|
||||
{
|
||||
(void)fcntl(fds[0], F_SETFL, O_NONBLOCK);
|
||||
(void)fcntl(fds[1], F_SETFL, O_NONBLOCK);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user