APUE-文件和目录(一)

时间:2022-06-21 12:16:58

4.1 函数stat

函数stat返回与此命名文件有关的信息结构。下面的代码实现了一个工具,显示树形目录结构,需要加两个参数,一个为目录名,一个为显示目录的深度。

#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
/***************************************************************/
 /*struct stat {*/
 /* unsigned long   st_dev; */    /* Device.  */
 /* unsigned long   st_ino;*/     /* File serial number.  */
 /* unsigned int    st_mode;  */  /* File mode.  */
 /* unsigned int    st_nlink;  */ /* Link count.  */
 /* unsigned int    st_uid;    */ /* User ID of the file's owner.  */
 /* unsigned int    st_gid;    */ /* Group ID of the file's group. */
 /* unsigned long   st_rdev;    *//* Device number, if device.  */
 /* unsigned long   __pad1;   */
 /* long        st_size;    *//* Size of file, in bytes.  */
 /* int     st_blksize; *//* Optimal block size for I/O.  */
 /* int     __pad2;         */
 /* long        st_blocks;*/  /* Number 512-byte blocks allocated. */
 /* long        st_atime;  */ /* Time of last access.  */
 /* unsigned long   st_atime_nsec; */
 /* long        st_mtime;  */ /* Time of last modification.  */
 /* unsigned long   st_mtime_nsec;*/
 /* long        st_ctime;  */ /* Time of last status change.  */
 /* unsigned long   st_ctime_nsec;  */
 /* unsigned int    __unused4;     */
 /* unsigned int    __unused5;*/
 /*                 };    */
 /* *************************************************************/

void printMode(unsigned int st_mode,int indent)
{
    int num = 0;
    for(;num<indent;num++)
    {
        printf("  ");
    }
    printf(S_ISDIR(st_mode)?"d":"-");
    printf(st_mode&S_IRUSR?"r":"-");
    printf(st_mode&S_IWUSR?"w":"-");
    printf(st_mode&S_IXUSR?"x":"-");
    printf(st_mode&S_IRGRP?"r":"-");
    printf(st_mode&S_IWGRP?"w":"-");
    printf(st_mode&S_IXGRP?"x":"-");
    printf(st_mode&S_IROTH?"r":"-");
    printf(st_mode&S_IWOTH?"w":"-");
    printf(st_mode&S_IXOTH?"x":"-");
}

void printFileName(char *name)
{
    printf(" %s\n",name);
}

void printUserName(unsigned int userId)
{
    struct passwd *pwd = getpwuid(userId);
    printf(" %s", pwd->pw_name);
}
void printGroupName(unsigned int grpId)
{
    struct  group *grp = getgrgid(grpId);
    printf(" %s" ,grp->gr_name);
}
void printSize(long size)
{
    printf(" %lu",size);
}
void printModifyTime(long mtime)
{
    /*char buf[100]={0};
    ctime_s(buf,26,mtime);
    printf(" %s",buf);*/
    printf(" %lu",mtime);
}
int ls(char *path,int depth,int indent)
{
    DIR *dhandle;
    struct dirent *file;
    struct stat st;
    if(!(dhandle=opendir(path)))
    {
        printf("error opendir %s\n",path);
        return -1;
    }
    while((file = readdir(dhandle))!=NULL)
    {
        int fullPathLen = strlen(path)+strlen(file->d_name)+1;
        if(strncmp(file->d_name,".",1)==0)
          continue;
        char *fullpath = (char*)malloc(fullPathLen+1);
        memset(fullpath,0,fullPathLen+1);
        strcpy(fullpath,path);
        strcat(fullpath,"/");
        strcat(fullpath,file->d_name);
        int rv = stat(fullpath,&st);
        if(rv<0)
        {
            return -1;
        }
        printMode(st.st_mode,indent);
        printUserName(st.st_uid);
        printGroupName(st.st_gid);
        printSize(st.st_size);
        printModifyTime(st.st_mtime);
        printFileName(file->d_name);

        if(S_ISDIR(st.st_mode)&& (depth-1>0))
        {
        ls(fullpath,depth-1,indent+1);
        }
        free(fullpath);
    }
    closedir(dhandle);
    return 0;
}

int main(int argc,char *argv[])
{
    int dep = atoi(argv[2]);
    ls(argv[1],dep,0);
    return 0;
}

运行如下命令

gcc stat.c

生成一个a.out可执行文件,运行如下命令:

harlan@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples$ ./a.out /github/ 2        
drwxrwxrwx harlan harlan 0 1494143291 3202C                                         
  drwxrwxrwx harlan harlan 0 1494143273 Doc                                         
  -rw-rw-rw- harlan harlan 828 1494143273 Readme.txt                                
  drwxrwxrwx harlan harlan 0 1494143279 SRC                                         
  drwxrwxrwx harlan harlan 0 1494143281 inc                                         
  -rw-rw-rw- harlan harlan 9700 1494143282 m3327.mdf                                
  -rw-rw-rw- harlan harlan 1182 1494143282 m3327boot.mdf                            
  drwxrwxrwx harlan harlan 0 1494143290 prj                                         
  drwxrwxrwx harlan harlan 0 1494143292 sdk                                         
drwxr-xr-x harlan harlan 0 1495673220 APUE                                          
  -rw-r--r-- harlan harlan 6 1493303590 README.md                                   
  -rwxrwxrwx root root 17478 1494424916 a.out                                       
  -rw-rw-rw- harlan harlan 4352 1494167949 apue.h                                   
  -rw-rw-rw- harlan harlan 2660400 1493735585 apue.h.gch                            
  drwxrwxrwx harlan harlan 0 1494248815 chapter_1                                   
  drwxrwxrwx harlan harlan 0 1495117067 chapter_2                                   
  drwxrwxrwx harlan harlan 0 1494509690 chapter_3                                   
  drwxrwxrwx harlan harlan 0 1495113400 chapter_4                                   
  drwxrwxr-x harlan harlan 0 1494944116 chapter_5                                   
  -rw-rw-rw- harlan harlan 2220 1494167949 err.c                                    
  drwxrwxr-x harlan harlan 0 1494769702 foo                                         
  -rw-rw-r-- harlan harlan 399 1494424891 go.c                                      
  -rw------- harlan harlan 1675 1494512317 key                                      
  -rw-r--r-- harlan harlan 404 1494512317 key.pub                                   
  -rw-rw-rw- harlan harlan 1501 1494116048 print.c                                  
  -rwx------ harlan harlan 1457 1493733958 tags                                     
  drwxrwxr-x harlan harlan 0 1494769702 testdir                                     
  -rw-r--r-- harlan harlan 4790 1495671977 vimrc.txt                                
-rw------- harlan harlan 1679 1493304485 pub                                        
-rw-r--r-- harlan harlan 402 1493304485 pub.pub                                     
drwxrwxr-x harlan harlan 0 1494511444 test                                          

4.2 文件类型

文件类型包括以下几种:

  1. 普通文件
  2. 目录文件
  3. 块特殊文件
  4. 字符特殊文件
  5. FIFO
  6. 套接字
  7. 符号链接

可以用图4-1中的宏确定文件类型,这些宏的参数是stat结构中的st_mode成员。
APUE-文件和目录(一)
可以用图4-2中的宏来从stat结构中确定IPC对象的类型。,它们的参数是指向stat结构的指针。

APUE-文件和目录(一)

4.3 设置用户ID和设置组ID

APUE-文件和目录(一)

  • 实际用户ID和实际组ID标识我们究竟是谁。
  • 有效用户ID、有效组ID以及附属组ID决定了我们的文件访问权限。
  • 保存的设置用户ID和保存的设置组ID在执行一个程序时包含了有效用户ID和有效组ID的副本。

通常,有效用户ID等于实际用户ID,有效组ID等于实际组ID。

我们可以在文件模式字(st_mode)中设置一个特殊标志,其含义是“当执行此文件时,将进程的有效用户ID设置为文件所有者的用户ID(st_uid)”,组ID也一样。在文件模式字中的这两位被称为设置用户组ID(set-user=ID)位设置组ID(set-group-ID)位

例如,passwd允许任意用户改变其口令,该程序是一个设置用户ID程序。见下面的展示:

harlan@DESKTOP-KU8C3K5:~$ ll /etc/passwd
-rw-r--r-- 1 root root 1219  5月  8 21:34 /etc/passwd
harlan@DESKTOP-KU8C3K5:~$ ll /usr/bin/passwd
-rwsr-xr-x 1 root root 47032  1月 27  2016 /usr/bin/passwd

任意用户修改口令需要写入/etc/passwd文件中,而此文件只有root用户才有写权限,/usr/bin/passwd用来执行写操作,可以看到这个程序将root用户指定为设置用户组ID,因此任意用户当执行此程序进行写文件的时候将拥有root权限。

4.5 文件访问权限

所有文件类型(目录·字符特别文件等)都有访问权限(access permission)。每个文件有9个访问权限,将它们分为3类,见图4-6。

APUE-文件和目录(一)

我们用chmod命令修改这9个权限位。它允许我们用u表示用户,用g表示组,用o表示其他
图4-6中的3类访问权限(读、写和执行)以各种不同的方式由不同函数使用。

  • 规则一,我们用名字打开任一类型的文件时,对该名字中包含的每一个目录,包括它可能隐含的当前工作目录都应具有执行权限。这就是为什么对于目录其执行权限位常被称为搜索位的原因。

    读权限允许我们读目录,获得在该目录中所有文件名的列表,这是原书中的话,看下面的例子:

    drwxr-xr-- root root 0 1496278552 test
    -rw-r--r-- root root 0 1496278548 1.txt
    -rw-r--r-- root root 0 1496278552 2.txt

    test文件夹的路径为/,我们现在在普通用户(harlan)下执行ls,看是否可以将test下面的文件名列出来。可以看到test文件夹和下面的两个文件对于其他用户有读权限。

先执行一个cd.

harlan@DESKTOP-KU8C3K5:~$ cd /test
bash: cd: /test: 权限不够

可见cd是需要执行权限的。

harlan@DESKTOP-KU8C3K5:~$ ls /test
1.txt  2.txt
harlan@DESKTOP-KU8C3K5:~$ ls -l /test
ls: 无法访问/test/1.txt: 权限不够
ls: 无法访问/test/2.txt: 权限不够
总用量 0
-????????? ? ? ? ?             ? 1.txt
-????????? ? ? ? ?             ? 2.txt

可见普通的ls是可以的,以列表方式列出文件信息就只列出了文件名。

为文件夹加上执行权限:

drwxr-xr-x root root 0 1496278552 test
  -rw-r--r-- root root 0 1496278548 1.txt
  -rw-r--r-- root root 0 1496278552 2.txt
harlan@DESKTOP-KU8C3K5:/$ cd /test
harlan@DESKTOP-KU8C3K5:/test$
harlan@DESKTOP-KU8C3K5:/test$cd ..
harlan@DESKTOP-KU8C3K5:/$ ls -l /test
总用量 0
-rw-r--r-- 1 root root 0  6月  1 08:55 1.txt
-rw-r--r-- 1 root root 0  6月  1 08:55 2.txt

cd执行成功,ls -l 也执行成功。

  • 对于一个文件的读权限决定了我们是否能够打开现有文件进行读操作。这与open函数的O_RDONLY和O_RDWR标志相关。
  • 对于一个文件的写权限决定了我们是否能够打开现有文件进行写操作。这与open函数的O_WRONLY和O_RDWR标志相关。
  • 为了在open函数中对一个文件指定O_TRUNC标志,必须对该文件具有写权限。
  • 为了在一个目录中创建新文件,必须对该目录具有写权限和执行权限。
  • 为了删除一个现有文件,必须对包含该文件的目录具有写和执行权限。对该文件不需要有读写权限。
    看下面的例子:
drwxr-xrwx root root 0 1496278552 test
  -rw-r----- root root 0 1496278548 1.txt
  -rw-r--r-- root root 0 1496278552 2.txt

在普通用户下删除1.txt:

harlan@DESKTOP-KU8C3K5:/test$ ll
总用量 0
-rw-r----- 1 root root 0  6月  1 08:55 1.txt
-rw-r--r-- 1 root root 0  6月  1 08:55 2.txt
harlan@DESKTOP-KU8C3K5:/test$ rm 1.txt
rm:是否删除有写保护的普通空文件 "1.txt"? y
harlan@DESKTOP-KU8C3K5:/test$ ll
总用量 0
-rw-r--r-- 1 root root 0  6月  1 08:55 2.txt
  • 如果用7个exec函数中的任何一个执行某个文件,都必须对该文件具有执行权限。该文件还必须是一个普通文件。

进程每打开、创建或者删除一个文件时,内核就进行文件访问权限测试,而这种测试可能涉及文件的所有者(st_uid和st_gid)、进程的有效ID(有效用户ID和有效组ID)以及进程的附属组ID(若支持的话)。两个所有者ID是文件的性质,而两个有效ID和附属组ID则是进程的性质。内核进行的具体测试如下。

APUE-文件和目录(一)

注意: 如果进程拥有此文件,则按用户访问权限批准或拒绝该进程对文件的访问——不查看组访问权限。类似的,如果进程不拥有该文件,而是属于某个组,则按照组权限批准或拒绝访问文件——不看其他用户权限。

4.6 新文件和目录的所有权

关于新目录的所有权规则与本节将说明的新文件所有权规则相同。

新文件的用户ID设置为进程的有效用户ID。关于组ID,POSIX.1允许实现选择下列之一作为新文件的组ID。

  1. 新文件的组ID可以是进程的有效组ID。
  2. 新文件的组ID可以是它所在目录的组ID。

看下面的例子:
普通用户下写一个c程序 4-7.c

-rw-rw-r-- 1 harlan harlan  290  6月  1 22:13 4-7.c
#include <unistd.h>                                                                    
#include <sys/types.h>                                                                 
#include <stdio.h>                                                                     
#include <fcntl.h>                                                                     
#define RWRWRW  (S_IRUSR |S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)                     
int main(void)                                                                         
{                                                                                      
    int fd = creat("test4-7.txt",RWRWRW);                                              
    printf("uid = %d,gid=%d,euid=%d,egid=%d\n",getuid(),getgid(),geteuid(),getegid()); 
}                                                                                      

编译生成可执行文件a.out

gcc 4-7.c
-rwxrwxr-x 1 harlan harlan 8780  6月  1 22:13 a.out

运行程序

harlan@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples$ ./a.out

输出:

uid = 1000,gid=1000,euid=1000,egid=1000

查看创建的文件s所属用户(harlan用户ID为1000):

-rw-rw-r-- 1 harlan harlan    0  6月  1 22:13 test4-7.txt

修改a.out可执行程序的拥有着为root,并设置保存的设置用户ID:

harlan@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples$ su
密码:
root@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples# chown root a.out
root@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples# ll a.out
-rwxrwxr-x 1 root harlan 8780  6月  1 22:13 a.out*
root@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples# chmod u+s a.out
root@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples# ll a.out
-rwsrwxr-x 1 root harlan 8780  6月  1 22:13 a.out*

跳转到harlan用户执行a.out:

harlan@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples$ ./a.out
uid = 1000,gid=1000,euid=0,egid=1000
harlan@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples$ ll test4-7.txt
-rw-rw-r-- 1 root harlan 0  6月  1 22:33 test4-7.txt

可以看到有效用户ID变为了0,因此创建的文件的用户变为了root。

在看一下新创建文件的组ID,将a.out的组设置为root,并且设置保存的设置组ID:

harlan@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples$ su
密码:
root@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples# chgrp root a.out
root@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples# chmod g+s a.out
root@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples# ll a.out
-rwxrwsr-x 1 root root 8780  6月  1 22:13 a.out*

回到harlan用户下执行a.out,可以看到生成文件所属组为root.

root@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples# su harlan
harlan@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples$ ./a.out
uid = 1000,gid=1000,euid=1000,egid=0
harlan@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples$ ll test4-7.txt
-rw-rw-r-- 1 harlan root 0  6月  1 22:41 test4-7.txt

可执行程序a.out所在目录myexamples的组为harlan

drwxrwxr-x 2 harlan harlan     0  6月  1 22:41 myexamples```

设置myexamples文件夹的设置组ID

harlan@DESKTOP-KU8C3K5:/github/APUE/chapter_4$ chmod g+s myexamples
drwxrwsr-x 2 harlan harlan     0  6月  1 22:41 myexamples

执行a.out之后发现生成文件的组变为了harlan,也就验证了APUE中的说法。

harlan@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples$ ./a.out
uid = 1000,gid=1000,euid=1000,egid=0
harlan@DESKTOP-KU8C3K5:/github/APUE/chapter_4/myexamples$ ll test4-7.txt
-rw-rw-r-- 1 harlan harlan 0  6月  2 08:14 test4-7.txt