C | Unix编程 | 3 - 文件

256 阅读6分钟

前言

本文是前面Unix编程系列的第3篇,

  • 主要描述文件特征或者文件性质,在编程中怎么去获取属性和更改属性。
  • 描述用户和组的一些概念,以及对应的文件权限操作。

文件信息

此处要讲的是关于文件的数据结构,在Unix中struct stat包含了所有类型文件的关键信息。很多系统调用核心都是在其基础之上操作。

stat(2)

获取文件的信息。

image.png

其中有个重要的struct stat,描述了文件的各种信息。

           struct stat {   
           
               dev_t     st_dev;         /* ID of device containing file */                                                                            
               ino_t     st_ino;         /* Inode number */                                                                                            
               mode_t    st_mode;        /* File type and mode */                                                                                      
               nlink_t   st_nlink;       /* Number of hard links */                                                                                    
               uid_t     st_uid;         /* User ID of owner */                                                                                        
               gid_t     st_gid;         /* Group ID of owner */                                                                                       
               dev_t     st_rdev;        /* Device ID (if special file) */                                                                             
               off_t     st_size;        /* Total size, in bytes */                                                                                    
               blksize_t st_blksize;     /* Block size for filesystem I/O */                                                                           
               blkcnt_t  st_blocks;      /* Number of 512B blocks allocated */                                                                         
                                                                                                                                                       
               /* Since Linux 2.6, the kernel supports nanosecond                                                                                      
                  precision for the following timestamp fields.                                                                                        
                  For the details before Linux 2.6, see NOTES. */                                                                                      
                                                                                                                                                       
               struct timespec st_atim;  /* Time of last access */                                                                                     
               struct timespec st_mtim;  /* Time of last modification */                                                                               
               struct timespec st_ctim;  /* Time of last status change */                                                                              
                                                                                                                                                       
           #define st_atime st_atim.tv_sec      /* Backward compatibility */                                                                           
           #define st_mtime st_mtim.tv_sec                                                                                                             
           #define st_ctime st_ctim.tv_sec                                                                                                             
           };   
st_dev:文件所在设备的ID;
st_ino:文件的Inode号;
st_mode:文件类型和权限模式;
st_nlink:硬链接数;
st_uid:文件所有者的用户ID;
st_gid:文件所有者的组ID;
st_rdev:设备文件的设备ID;
st_size:文件大小(字节数);
st_blksize:文件系统I/O的块大小;
st_blocks:文件分配的块数。

文件的类型 struct stat:st_mode

image.png 其中st_mode文件类型有如下几种类型

           
    S_IFMT     0170000   bit mask for the file type bit field   掩码                                                                                

//下面为列举的文件类型                                                               
    S_IFSOCK   0140000   socket           套接字(socket)类型,用于进程之间的通信;                                                                                                               
    S_IFLNK    0120000   symbolic link    符号链接(symbolic link)类型,常用于虚拟机、容器等环境下的软连接;                                                                                                       
    S_IFREG    0100000   regular file     普通文件(regular file)类型,最常见的文件类型;                                                                                                      
    S_IFBLK    0060000   block device     块设备文件(block device)类型,如硬盘、U盘等存储设备;                                                                                                      
    S_IFDIR    0040000   directory        目录(directory)类型,用于存放其他文件和目录;                                                                                                      
    S_IFCHR    0020000   character device 字符设备文件(character device)类型,如键盘、鼠标等输入设备;                                                                                                      
    S_IFIFO    0010000   FIFO             管道(FIFO)类型,可实现进程之间的通信

代码片段

#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/sysmacros.h>

int
main(int argc, char *argv[]) {
  struct stat sb;

  if (argc != 2) {
   fprintf(stderr, "Usage: %s <pathname>\n", argv[0]);
   exit(EXIT_FAILURE);
  }

  if (lstat(argv[1], &sb) == -1) {
   perror("lstat");
   exit(EXIT_FAILURE);
  }

  printf("ID of containing device:  [%lx,%lx]\n",
         (long) major(sb.st_dev), (long) minor(sb.st_dev));

  printf("File type:                ");

  // switch (sb.st_mode & S_IFMT) {
  // case S_IFBLK: printf("block device\n");
  //   break;
  // case S_IFCHR: printf("character device\n");
  //   break;
  // case S_IFDIR: printf("directory\n");
  //   break;
  // case S_IFIFO: printf("FIFO/pipe\n");
  //   break;
  // case S_IFLNK: printf("symlink\n");
  //   break;
  // case S_IFREG: printf("regular file\n");
  //   break;
  // case S_IFSOCK: printf("socket\n");
  //   break;
  // default: printf("unknown?\n");
  //   break;
  // }

# define S_ISSOCK(mode) __S_ISTYPE((mode), __S_IFSOCK)
  if (S_ISREG(sb.st_mode))
   printf("regular file\n");
  else if (S_ISDIR(sb.st_mode))
   printf("directory\n");
  else if (S_ISCHR(sb.st_mode))
   printf("character special\n");
  else if (S_ISBLK(sb.st_mode))
   printf("block special\n");
  else if (S_ISFIFO(sb.st_mode))
   printf("FIFO\n");
  else if (S_ISLNK(sb.st_mode))
   printf("symbolic link\n");
  else if (S_ISSOCK(sb.st_mode))
   printf("socket\n");
  else
   printf("unknown\n");

  printf("I-node number:            %ld\n", (long) sb.st_ino);

  printf("Mode:                     %lo (octal)\n",
         (unsigned long) sb.st_mode);

  printf("Link count:               %ld\n", (long) sb.st_nlink);
  printf("Ownership:                UID=%ld   GID=%ld\n",
         (long) sb.st_uid, (long) sb.st_gid);

  printf("Preferred I/O block size: %ld bytes\n",
         (long) sb.st_blksize);
  printf("File size:                %lld bytes\n",
         (long long) sb.st_size);
  printf("Blocks allocated:         %lld\n",
         (long long) sb.st_blocks);

  printf("Last status change:       %s", ctime(&sb.st_ctime));
  printf("Last file access:         %s", ctime(&sb.st_atime));
  printf("Last file modification:   %s", ctime(&sb.st_mtime));

  exit(EXIT_SUCCESS);
}

用户UIDs和组GIDs

struct stat: st_mode, st_uid, st_gid

每一个进程拥有6个或更多IDs与之关联

实际用户 ID
实际组 ID
我们实际上是谁
有效用户 ID
有效组 ID
附属组 ID
用于文件访问权限检查
保存的设置用户 ID
保存的设置组 ID
由exec函数保存
  • 当文件被设置为setuid时,将有效的用户ID设置为st_uid。
  • 当文件为setgid时,将有效的组ID设置为st_gid。
  • st_uid和st_gid总是指定文件的所有者和组所有者,不管它是setuid还是setgid。

image.png

文件的权限模式

它由10个字符组成,包括第1个特殊字符和后面的9个普通字符,每个字符代表一种权限或者文件类型。其中:

  1. 第一个字符代表文件类型。在这个例子中,这个文件是一个可执行文件,并且设置了 setuid 位。具体来说:

    • - 代表常规文件;
    • d 代表目录;
    • l 代表符号链接(软链接);
    • s 代表套接字(socket);
    • p 代表命名管道(named pipe);
    • c 代表字符设备文件;
    • b 代表块设备文件。
  2. 后面的9个字符每三个一组,分别表示对该文件的读、写、执行权限。其中:

    • r 表示可读取(read);
    • w 表示可写入(write);
    • x 表示可以执行文件或进入目录(execute);
    • - 表示无对应权限。
  3. 特殊权限位:

    • s 表示 setuid/setgid 位。对于可执行文件,设置 setuid 位表示在执行该文件时,使用该文件的所有者的权限来执行。
    • S 表示 setuid/setgid 位被设置,但与可执行文件无关(例如,对于一个文件夹)。
    • t 表示 sticky 位。对于一个目录,设置了 sticky 位后,只有目录的所有者和根用户才能删除该目录中的文件。
    • T 表示 sticky 位被设置,但与文件夹无关(例如,在一个空的文件夹中)。

-rwsr-xr-x 表示该文件是一个可执行文件,并且设置了 setuid 位,所有者具有读、写、执行权限,组用户和其他用户具有读和执行权限。

getuid(2),setuid(2)

       #include <unistd.h>                                                                                                                             
       #include <sys/types.h>                                                                                                                                                           
       uid_t getuid(void);                                                                                                                             
       uid_t geteuid(void);
       //Returns: uid_t; never errors
       
       
       int setuid(uid_t uid); 
       int seteuid(uid_t uid);
       //Returns: 0 if OK, -1 on error
       
       

关于UIDs

  • 每个进程都有一个有效的UID和一个真实的UID,一个有效的GID和一个真实的GID
  • 如果在权限中设置了setuid (setgid)位,则在执行时,有效UID (GID)将成为文件结构stat的st_uid (st_gid)
  • 可以通过seteuid(2)在它们之间切换
  • setuid(2)不可撤销地设置了两者。
  • 有效的UID和组ID是用于文件权限检查的UID和组ID,但可以通过access(2)系统调用来检查真实的ID是否具有权限

chmod(2) and chown(2)

       #include    <sys/stat.h>  
       int chmod(const char *pathname, mode_t mode);         
       int fchmod(int fd, mode_t mode);                                                                                                                
                                                                                                                                                       
       #include  <fcntl.h>           /* Definition of AT_* constants */                                                                                 
       #include  <sys/stat.h>                                          
       int fchmodat(int dirfd, const char *pathname, mode_t mode, int flags);   

用于修改文件权限的系统函数。其中mode_t mode类型可以对应设置值如下(可以ORing组合):

       The new file mode is specified in mode, which is a bit mask created by ORing together zero or more of the following:                            
                                                                                                                                                       
       S_ISUID  (04000)  set-user-ID (set process effective user ID on execve(2))                                                                      
                                                                                                                                                       
       S_ISGID  (02000)  set-group-ID (set process effective group ID on execve(2); mandatory locking, as described in fcntl(2); take a new  file's    
                         group from parent directory, as described in chown(2) and mkdir(2))                                                           
                                                                                                                                                       
       S_ISVTX  (01000)  sticky bit (restricted deletion flag, as described in unlink(2))                                                              
                                                                                                                                                       
       S_IRUSR  (00400)  read by owner                                                                                                                 
                                                                                                                                                       
       S_IWUSR  (00200)  write by owner                                                                                                                
                                                                                                                                                       
       S_IXUSR  (00100)  execute/search by owner ("search" applies for directories, and means that entries within the directory can be accessed)       
                                                                                                                                                       
       S_IRGRP  (00040)  read by group                                                                                                                 
                                                                                                                                                       
       S_IWGRP  (00020)  write by group                                                                                                                
                                                                                                                                                       
       S_IXGRP  (00010)  execute/search by group                                                                                                       
                                                                                                                                                       
       S_IROTH  (00004)  read by others                                                                                                                
                                                                                                                                                       
       S_IWOTH  (00002)  write by others                                                                                                               
                                                                                                                                                       
       S_IXOTH  (00001)  execute/search by others    

代码片段

#include <sys/types.h>
#include <sys/stat.h>

#include <stdio.h>
#include <stdlib.h>

int
main() {
   struct stat sbuf;

   if (stat("file", &sbuf) == -1) {
      perror("can't stat file");
      exit(EXIT_FAILURE);
   }

   /* 关闭所有者读取权限并打开 setgid */
   if (chmod("file", (sbuf.st_mode & ~S_IRUSR) | S_ISGID) == -1) {
      perror("can't chmod file");
      exit(EXIT_FAILURE);
   }

   /* 将绝对模式设置为 rw-r--r- */
   if (chmod("file1", S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1) {
      perror("can't chmod file1");
      exit(EXIT_FAILURE);
   }

   exit(EXIT_SUCCESS);
}

关于权限

  • 只有根用户和文件所有者可以更改其权限。
  • 只有root可以更改文件的所有者,但所有者可以更改文件的组所有者
  • 上面的chmod(2) chown(2)对应于系统的命令chmod chown使用,详细使用方法和在程序中使用函数,可以查阅文档手册。

总结

Unix中了解文件的特征是必不可少的内容。了解掌握其中的关键点有助于更好的了解系统。