glibc源码分析(三)文件系统系统调用

最新博客地址 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;
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值