最新博客地址 shankusu.me
以下内容转载自
https://zhuanlan.zhihu.com/p/31496984
前文详细介绍了glibc封装系统调用的方法,在本文中我将向大家讲解glibc系统调用封装的具体的例子。这些例子都是关于文件系统的。
1.1 文件的创建与删除
linux支持7种文件:普通文件,目录文件,字符设备文件,块设备文件,管道文件,套接字文件,符号链接文件。每种文件的创建与删除都有对应系统调用。glibc封装了这些系统调用。
创建与删除文件的系统调用有:creat,unlink,mkdir,rmdir,mknod,symlink,link。与创建与删除文件的系统调用有关的系统调用有:umask。
其中unlink,mkdir,rmdir,symlink,link,umask是用脚本生成的。
unlink系统调用的封装代码
#define SYSCALL_NAME unlink
#define SYSCALL_NARGS 1
#define SYSCALL_SYMBOL __unlink
#define SYSCALL_CANCELLABLE 0
#define SYSCALL_NOERRNO 0
#define SYSCALL_ERRVAL 0
#include <syscall-template.S>
weak_alias (__unlink, unlink)
hidden_weak (unlink)
mkdir系统调用的封装代码
#define SYSCALL_NAME mkdir
#define SYSCALL_NARGS 2
#define SYSCALL_SYMBOL __mkdir
#define SYSCALL_CANCELLABLE 0
#define SYSCALL_NOERRNO 0
#define SYSCALL_ERRVAL 0
#include <syscall-template.S>
weak_alias (__mkdir, mkdir)
hidden_weak (mkdir)
rmdir系统调用的封装代码
#define SYSCALL_NAME rmdir
#define SYSCALL_NARGS 1
#define SYSCALL_SYMBOL __rmdir
#define SYSCALL_CANCELLABLE 0
#define SYSCALL_NOERRNO 0
#define SYSCALL_ERRVAL 0
#include <syscall-template.S>
weak_alias (__rmdir, rmdir)
hidden_weak (rmdir)
symlink系统调用的封装代码
#define SYSCALL_NAME symlink
#define SYSCALL_NARGS 2
#define SYSCALL_SYMBOL __symlink
#define SYSCALL_CANCELLABLE 0
#define SYSCALL_NOERRNO 0
#define SYSCALL_ERRVAL 0
#include <syscall-template.S>
weak_alias (__symlink, symlink)
hidden_weak (symlink)
link系统调用的封装代码
#define SYSCALL_NAME link
#define SYSCALL_NARGS 2
#define SYSCALL_SYMBOL __link
#define SYSCALL_CANCELLABLE 0
#define SYSCALL_NOERRNO 0
#define SYSCALL_ERRVAL 0
#include <syscall-template.S>
weak_alias (__link, link)
hidden_weak (link)
umask系统调用的封装代码
#define SYSCALL_NAME umask
#define SYSCALL_NARGS 1
#define SYSCALL_SYMBOL __umask
#define SYSCALL_CANCELLABLE 0
#define SYSCALL_NOERRNO 1
#define SYSCALL_ERRVAL 0
#include <syscall-template.S>
weak_alias (__umask, umask)
hidden_weak (umask)
mknod系统调用是.c文件封装的。它的源码如下:
int
attribute_hidden
__mknod (const char *path, mode_t mode, dev_t dev)
{
return __xmknod (_MKNOD_VER, path, mode, &dev);
}
weak_hidden_alias (__mknod, mknod)
int
__xmknod (int vers, const char *path, mode_t mode, dev_t *dev)
{
unsigned long long int k_dev;
if (vers != _MKNOD_VER)
return INLINE_SYSCALL_ERROR_RETURN_VALUE (EINVAL);
/* We must convert the value to dev_t type used by the kernel. */
k_dev = (*dev) & ((1ULL << 32) - 1);
if (k_dev != *dev)
return INLINE_SYSCALL_ERROR_RETURN_VALUE (EINVAL);
return INLINE_SYSCALL (mknod, 3, path, mode, (unsigned int) k_dev);
}
weak_alias (__xmknod, _xmknod)
libc_hidden_def (__xmknod)
__mknod函数调用了__xmknod函数,__xmknod函数中使用INLINE_SYSCALL宏完成mknod系统调用的调用。
creat系统调用是.c文件封装的。它的源码如下:
int
__creat (const char *file, mode_t mode)
{
return SYSCALL_CANCEL (creat, file, mode);
}
weak_alias (__creat, creat)
SYSCALL_CANCEL 宏调用了creat系统调用。SYSCALL_CANCEL宏做了单线程与多线程下调用系统调用的处理。
#define SYSCALL_CANCEL(...) \
({ \
long int sc_ret; \
if (SINGLE_THREAD_P) \
sc_ret = INLINE_SYSCALL_CALL (__VA_ARGS__); \
else \
{ \
int sc_cancel_oldtype = LIBC_CANCEL_ASYNC (); \
sc_ret = INLINE_SYSCALL_CALL (__VA_ARGS__); \
LIBC_CANCEL_RESET (sc_cancel_oldtype); \
} \
sc_ret; \
})
SYSCALL_CANCEL宏调用INLINE_SYSCALL_CALL宏完成系统调用的封装。
INLINE_SYSCALL_CALL宏
#define INLINE_SYSCALL_CALL(...) \
__INLINE_SYSCALL_DISP (__INLINE_SYSCALL, __VA_ARGS__)
#define __INLINE_SYSCALL_DISP(b,...) \
__SYSCALL_CONCAT (b,__INLINE_SYSCALL_NARGS(__VA_ARGS__))(__VA_ARGS__)
#define __SYSCALL_CONCAT_X(a,b) a##b //连接两个宏
#define __SYSCALL_CONCAT(a,b) __SYSCALL_CONCAT_X (a, b)
#define __INLINE_SYSCALL_NARGS_X(a,b,c,d,e,f,g,h,n,...) n
#define __INLINE_SYSCALL_NARGS(...) \ //返回系统调用参数个数
__INLINE_SYSCALL_NARGS_X (__VA_ARGS__,7,6,5,4,3,2,1,0,)
#define __INLINE_SYSCALL0(name) \
INLINE_SYSCALL (name, 0)
#define __INLINE_SYSCALL1(name, a1) \
INLINE_SYSCALL (name, 1, a1)
#define __INLINE_SYSCALL2(name, a1, a2) \
INLINE_SYSCALL (name, 2, a1, a2)
#define __INLINE_SYSCALL3(name, a1, a2, a3) \
INLINE_SYSCALL (name, 3, a1, a2, a3)
#define __INLINE_SYSCALL4(name, a1, a2, a3, a4) \
INLINE_SYSCALL (name, 4, a1, a2, a3, a4)
#define __INLINE_SYSCALL5(name, a1, a2, a3, a4, a5) \
INLINE_SYSCALL (name, 5, a1, a2, a3, a4, a5)
#define __INLINE_SYSCALL6(name, a1, a2, a3, a4, a5, a6) \
INLINE_SYSCALL (name, 6, a1, a2, a3, a4, a5, a6)
#define __INLINE_SYSCALL7(name, a1, a2, a3, a4, a5, a6, a7) \
INLINE_SYSCALL (name, 7, a1, a2, a3, a4, a5, a6, a7)
最终,调用INLINE_SYSCALL 宏完成系统调用封装。
1.2 文件属性的获取与修改
linux中每种文件都有其属性,属性可以获取与修改。unix提供了系统调用用于属性的获取与修改。
1.2.1 文件属性获取
linux中关于文件属性获取的系统调用有9个:oldstat(18),oldfstat(28),oldlstat(84),stat(106),lstat(107),fstat(108),stat64(195),lstat64(196),fstat64(197)。glibc封装了6个系统调用,它们分别是:stat,lstat,fstat,stat64,lstat64,fstat64。这6个系统调用都是使用.c文件封装的。
stat函数的源码如下:
#undef stat
int
attribute_hidden
__stat (const char *file, struct stat *buf)
{
return __xstat (_STAT_VER, file, buf);
}
weak_hidden_alias (__stat, stat)
int
__xstat (int vers, const char *name, struct stat *buf)
{
int result;
if (vers == _STAT_VER_KERNEL)
return INLINE_SYSCALL (stat, 2, name, buf);
{
struct stat64 buf64;
INTERNAL_SYSCALL_DECL (err);
result = INTERNAL_SYSCALL (stat64, err, 2, name, &buf64);
if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (result, err)))
return INLINE_SYSCALL_ERROR_RETURN_VALUE (INTERNAL_SYSCALL_ERRNO (result,
err));
else
return __xstat32_conv (vers, &buf64, buf);
}
}
hidden_def (__xstat)
weak_alias (__xstat, _xstat);
__stat函数调用了__xstat函数。__xstat函数使用INTERNAL_SYSCALL宏调用了stat64系统调用。如果调用成功则转化stat64结构数据为stat结构数据。
__xstat32_conv函数源码如下:
int
__xstat32_conv (int vers, struct stat64 *kbuf, struct stat *buf)
{
switch (vers)
{
case _STAT_VER_LINUX:
{
buf->st_dev = kbuf->st_dev;
#ifdef _HAVE_STAT___PAD1
buf->__pad1 = 0;
#endif
#ifdef _HAVE_STAT64___ST_INO
# if !__ASSUME_ST_INO_64_BIT
if (kbuf->st_ino == 0)
buf->st_ino = kbuf->__st_ino;
else
# endif
{
buf->st_ino = kbuf->st_ino;
if (sizeof (buf->st_ino) != sizeof (kbuf->st_ino)
&& buf->st_ino != kbuf->st_ino)
return INLINE_SYSCALL_ERROR_RETURN_VALUE (EOVERFLOW);
}
#else
buf->st_ino = kbuf->st_ino;
if (sizeof (buf->st_ino) != sizeof (kbuf->st_ino)
&& buf->st_ino != kbuf->st_ino)
return INLINE_SYSCALL_ERROR_RETURN_VALUE (EOVERFLOW);
#endif
buf->st_mode = kbuf->st_mode;
buf->st_nlink = kbuf->st_nlink;
buf->st_uid = kbuf->st_uid;
buf->st_gid = kbuf->st_gid;
buf->st_rdev = kbuf->st_rdev;
#ifdef _HAVE_STAT___PAD2
buf->__pad2 = 0;
#endif
buf->st_size = kbuf->st_size;
/* Check for overflow. */
if (sizeof (buf->st_size) != sizeof (kbuf->st_size)
&& buf->st_size != kbuf->st_size)
return INLINE_SYSCALL_ERROR_RETURN_VALUE (EOVERFLOW);
buf->st_blksize = kbuf->st_blksize;
buf->st_blocks = kbuf->st_blocks;
/* Check for overflow. */
if (sizeof (buf->st_blocks) != sizeof (kbuf->st_blocks)
&& buf->st_blocks != kbuf->st_blocks)
return INLINE_SYSCALL_ERROR_RETURN_VALUE (EOVERFLOW);
#ifdef _HAVE_STAT_NSEC
buf->st_atim.tv_sec = kbuf->st_atim.tv_sec;
buf->st_atim.tv_nsec = kbuf->st_atim.tv_nsec;
buf->st_mtim.tv_sec = kbuf->st_mtim.tv_sec;
buf->st_mtim.tv_nsec = kbuf->st_mtim.tv_nsec;
buf->st_ctim.tv_sec = kbuf->st_ctim.tv_sec;
buf->st_ctim.tv_nsec = kbuf->st_ctim.tv_nsec;
#else
buf->st_atime = kbuf->st_atime;
buf->st_mtime = kbuf->st_mtime;
buf->st_ctime = kbuf->st_ctime;
#endif
#ifdef _HAVE_STAT___UNUSED1
buf->__glibc_reserved1 = 0;
#endif
#ifdef _HAVE_STAT___UNUSED2
buf->__glibc_reserved2 = 0;
#endif
#ifdef _HAVE_STAT___UNUSED3
buf->__glibc_reserved3 = 0;
#endif
#ifdef _HAVE_STAT___UNUSED4
buf->__glibc_reserved4 = 0;
#endif
#ifdef _HAVE_STAT___UNUSED5
buf->__glibc_reserved5 = 0;
#endif
}
break;
/* If struct stat64 is different from struct stat then
_STAT_VER_KERNEL does not make sense. */
case _STAT_VER_KERNEL:
default:
return INLINE_SYSCALL_ERROR_RETURN_VALUE (EINVAL);
}
return 0;
}
#define _STAT_VER_LINUX 3
#define _STAT_VER _STAT_VER_LINUX
lstat函数源码如下:
#undef lstat
#undef __lstat
int
attribute_hidden
__lstat (const char *file, struct stat *buf)
{
return __lxstat (_STAT_VER, file, buf);
}
weak_hidden_alias (__lstat, lstat)
int
__lxstat (int vers, const char *name, struct stat *buf)
{
int result;
if (vers == _STAT_VER_KERNEL)
return INLINE_SYSCALL (lstat, 2, name, buf);
{
struct stat64 buf64;
INTERNAL_SYSCALL_DECL (err);
result = INTERNAL_SYSCALL (lstat64, err, 2, name, &buf64);
if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (result, err)))
return INLINE_SYSCALL_ERROR_RETURN_VALUE (INTERNAL_SYSCALL_ERRNO (result,
err));
else
return __xstat32_conv (vers, &buf64, buf);
}
}
hidden_def (__lxstat)
weak_alias (__lxstat, _lxstat);
__lstat函数调用了__lxstat函数。__lxstat函数使用INTERNAL_SYSCALL宏调用了lstat64系统调用。如果调用成功则转化stat64结构数据为stat结构数据。
fstat函数源码如下:
#undef fstat
#undef __fstat
int
attribute_hidden
__fstat (int fd, struct stat *buf)
{
return __fxstat (_STAT_VER, fd, buf);
}
weak_hidden_alias (__fstat, fstat)
int
__fxstat (int vers, int fd, struct stat *buf)
{
int result;
if (vers == _STAT_VER_KERNEL)
return INLINE_SYSCALL (fstat, 2, fd, buf);
{
struct stat64 buf64;
INTERNAL_SYSCALL_DECL (err);
result = INTERNAL_SYSCALL (fstat64, err, 2, fd, &buf64);
if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (result, err)))
return INLINE_SYSCALL_ERROR_RETURN_VALUE (INTERNAL_SYSCALL_ERRNO (result,
err));
else
return __xstat32_conv (vers, &buf64, buf);
}
}
hidden_def (__fxstat)
weak_alias (__fxstat, _fxstat);
__fstat函数调用了__fxstat函数。__fxstat函数使用INTERNAL_SYSCALL宏调用了fstat64系统调用。如果调用成功则转化stat64结构数据为stat结构数据。
stat64函数源码如下:
#undef stat64
int
attribute_hidden
stat64 (const char *file, struct stat64 *buf)
{
return __xstat64 (_STAT_VER, file, buf);
}
int
___xstat64 (int vers, const char *name, struct stat64 *buf)
{
int result;
result = INLINE_SYSCALL (stat64, 2, name, buf);
return result;
}
stat64函数调用了__xstat64函数。__xstat64函数使用INLINE_SYSCALL宏调用了stat64系统调用。
lstat64函数源码如下:
#undef lstat64
int
attribute_hidden
lstat64 (const char *file, struct stat64 *buf)
{
return __lxstat64 (_STAT_VER, file, buf);
}
int
___lxstat64 (int vers, const char *name, struct stat64 *buf)
{
int result;
result = INLINE_SYSCALL (lstat64, 2, name, buf);
return result;
}
lstat64函数调用了__lxstat64函数。__lxstat64函数使用INLINE_SYSCALL宏调用了lstat64系统调用。
fstat函数源码如下:
#undef fstat64
int
attribute_hidden
fstat64 (int fd, struct stat64 *buf)
{
return __fxstat64 (_STAT_VER, fd, buf);
}
int
___fxstat64 (int vers, int fd, struct stat64 *buf)
{
int result;
result = INLINE_SYSCALL (fstat64, 2, fd, buf);
return result;
}
fstat64函数调用了__fxstat64函数。__fxstat64函数使用INLINE_SYSCALL宏调用了fstat64系统调用。
1.2.2 测试文件权限
linux中access系统调用用于测试文件权限。glibc封装了该系统调用。
access函数源码:
int
__access (const char *file, int type)
{
return INLINE_SYSCALL_CALL (access, file, type);
}
weak_alias (__access, access)
1.2.3 修改文件权限
linux中关于修改文件权限的系统调用有2个,它们分别是:chmod(15),fchmod(94)。glibc封装了这两个系统调用。它们都是使用脚本封装的。
chmod系统调用的封装代码:
#define SYSCALL_NAME chmod
#define SYSCALL_NARGS 2
#define SYSCALL_SYMBOL __chmod
#define SYSCALL_CANCELLABLE 0
#define SYSCALL_NOERRNO 0
#define SYSCALL_ERRVAL 0
#include <syscall-template.S>
weak_alias (__chmod, chmod)
hidden_weak (chmod)
fchmod系统调用的封装代码:
#define SYSCALL_NAME fchmod
#define SYSCALL_NARGS 2
#define SYSCALL_SYMBOL __fchmod
#define SYSCALL_CANCELLABLE 0
#define SYSCALL_NOERRNO 0
#define SYSCALL_ERRVAL 0
#include <syscall-template.S>
weak_alias (__fchmod, fchmod)
hidden_weak (fchmod)
1.2.4 修改文件用户ID及组用户ID
linux中关于chown的系统调用有6个,它们分别是lchown(16),fchown(95),chown(182),lchown32(198),fchown32(207),chown32(212)。它们都是用于改变文件的用户ID和组用户ID。chown系列系统调用只能将用户ID和组用户ID改为16位整数。chown32系列系统调用则能将用户ID和组用户ID改为32位整数。
glibc封装了3个系统调用,分别封装为chown函数,fchown函数,lchown函数。它们都是通过脚本封装的。
chown函数的封装代码:
#define SYSCALL_NAME chown32
#define SYSCALL_NARGS 3
#define SYSCALL_SYMBOL __chown
#define SYSCALL_CANCELLABLE 0
#define SYSCALL_NOERRNO 0
#define SYSCALL_ERRVAL 0
#include <syscall-template.S>
#include <shlib-compat.h>
#if IS_IN (libc)
versioned_symbol (libc, __chown, chown, GLIBC_2_1)
#else
strong_alias (__chown, chown)
#endif
chown函数封装的是chown32系统调用。
fchown函数的封装代码:
#define SYSCALL_NAME fchown32
#define SYSCALL_NARGS 3
#define SYSCALL_SYMBOL __fchown
#define SYSCALL_CANCELLABLE 0
#define SYSCALL_NOERRNO 0
#define SYSCALL_ERRVAL 0
#include <syscall-template.S>
weak_alias (__fchown, fchown)
hidden_weak (fchown)
fchown函数封装的是fchown32系统调用。
lchown函数的封装代码:
#define SYSCALL_NAME lchown32
#define SYSCALL_NARGS 3
#define SYSCALL_SYMBOL __lchown
#define SYSCALL_CANCELLABLE 0
#define SYSCALL_NOERRNO 0
#define SYSCALL_ERRVAL 0
#include <syscall-template.S>
#include <shlib-compat.h>
#if IS_IN (libc)
versioned_symbol (libc, __lchown, lchown, GLIBC_2_0)
#else
strong_alias (__lchown, lchown)
#endif
#if defined SHARED && IS_IN (libc)
strong_alias (__lchown, __lchown_1)
compat_symbol (libc, __lchown_1, chown, GLIBC_2_0)
#endif
lchown函数封装的是lchown32系统调用。
1.2.5 修改文件时间
linux中关于修改文件时间的系统调用有2个:utime(30),utimes(271)。glibc封装了这2个系统调用。utime系统调用是用脚本封装的,utimes系统调用是用.c文件封装的。
utime系统调用封装代码:
#define SYSCALL_NAME utime
#define SYSCALL_NARGS 2
#define SYSCALL_SYMBOL utime
#define SYSCALL_CANCELLABLE 0
#define SYSCALL_NOERRNO 0
#define SYSCALL_ERRVAL 0
#include <syscall-template.S>
utimes函数源码:
int
__utimes (const char *file, const struct timeval tvp[2])
{
/* Avoid implicit array coercion in syscall macros. */
return INLINE_SYSCALL (utimes, 2, file, &tvp[0]);
}
weak_alias (__utimes, utimes)
1.2.6 截断文件
linux中截断文件的系统调用有4个:truncate(92),ftruncate(93),truncate64(193),ftruncate64(194)。glibc封装了这4个系统调用。
truncate函数的源码:
int
__truncate (const char *path, off_t length)
{
return INLINE_SYSCALL_CALL (truncate, path, length);
}
weak_alias (__truncate, truncate)
ftruncate函数的源码:
int
__ftruncate (int fd, off_t length)
{
return INLINE_SYSCALL_CALL (ftruncate, fd, length);
}
weak_alias (__ftruncate, ftruncate)
truncate64函数的源码:
int
__truncate64 (const char *path, off64_t length)
{
return INLINE_SYSCALL_CALL (truncate64, path,
__ALIGNMENT_ARG SYSCALL_LL64 (length));
}
weak_alias (__truncate64, truncate64)
#ifdef __OFF_T_MATCHES_OFF64_T
weak_alias (__truncate64, truncate);
#endif
#define SYSCALL_LL64(val) \
__LONG_LONG_PAIR ((long) ((val) >> 32), (long) ((val) & 0xffffffff))
#if __BYTE_ORDER == __LITTLE_ENDIAN
# define __LONG_LONG_PAIR(HI, LO) LO, HI
#elif __BYTE_ORDER == __BIG_ENDIAN
# define __LONG_LONG_PAIR(HI, LO) HI, LO
#endif
参数length是64位数据,需要转换为两个32位数据传给系统调用。源码中SYSCALL_LL64宏完成了这个处理。SYSCALL_LL64将64位数据转为两个32位数据,并根据是小端还是大端决定传入的顺序。
ftruncate64函数源码:
int
__ftruncate64 (int fd, off64_t length)
{
return INLINE_SYSCALL_CALL (ftruncate64, fd,
__ALIGNMENT_ARG SYSCALL_LL64 (length));
}
weak_alias (__ftruncate64, ftruncate64)
1.2.7 修改文件标志位
在一些系统上(mac os)文件属性中还有一项---标志位字段。标志位可以标识文件是否隐藏,文件是否只添加等。不过,在linux中没有这一项,所以相关的系统调用只返回错误值。
chflags
int
chflags (const char *file, unsigned long int flags)
{
if (file == NULL)
{
__set_errno (EINVAL);
return -1;
}
__set_errno (ENOSYS);
return -1;
}
fchflags
int
fchflags (int fd, unsigned long int flags)
{
if (fd < 0)
{
__set_errno (EINVAL);
return -1;
}
__set_errno (ENOSYS);
return -1;
}