APUE学习笔记:第四章 文件和目录

4.1 引言html

本章将描述文件的特征和文件的性质node

 

4.2 stat、fstat和lstat函数shell

#include<sys/stat.h>

int stat(const char *restrict pathname,struct stat *restrict buf);

int fstat(int filedes,struct stat *buf)

int lstat(const char *restrict pathname,struct stat *restrict buf);

                三个函数的返回值:若成功则返回0,若出错则返回-1

一旦给出pathname,stat函数就返回与此命名文件有关的信息结构。数组

fstat函数获取已在描述符filedes上打开文件的有关信息。安全

lstat相似于stat,可是当命名的文件是一个符号连接时,lstat返回该符号连接的有关信息,而不是由该符号连接引用文件的信息网络

 

第二个参数buf是指针,它指向一个咱们必须提供的结构。这些函数填写由buf指向的结构。该结构的实际定义可能随实现有所不一样,但其基本形式是:数据结构

struct stat{
            mode_t    st_mode;   //file type&mode (permissions)
            ino_t       st_ino;      //i-node number
            dev_t       st_dev;    //device number(file system)
            dev_t       st_rdev;    //device number for special files
            nlink_t     st_ulink;    //number of links
            uid_t        st_uid;    //user ID of owner
            gid_t        st_gid;    //group ID of owner
            off_t        st_size;    //size in bytes, for regular files
            time_t      st_atime    //time of last access
            time_t      st_mtime    //time of last modification
            time_t      st_ctime;    //time of last file status change
            blksize_t   st_blksize;    //best I/O block size
            blkcnt_t    st_blocks;    //number of disk blocks allocated
};//该结构中每个成员都是基本系统数据类型

使用stat函数最多的多是ls -l命令,用其能够得到有关一个文件的全部信息app

 

4.3 文件类型socket

UNIX系统的大多数文件是普通文件或目录,可是也有另一些文件类型。文件类型包括以下几种:ide

(1)普通文件  S_ISREG()

(2)目录文件  S_ISDIR()

(3)块特殊文件。这种文件类型提供对设备(例如磁盘)带缓冲的访问,每次访问以固定长度为单位进行  S_ISCHR()

(4)字符特殊文件。这种文件类型提供对设备不带缓冲的访问,每次访问长度可变。系统中的全部设备要么是字符特殊文件,要么是块特殊文件  S_ISBLK()

(5)FIFO。这种类型文件用于进程间通讯,有时也称为命名管道。  S_ISFIFO()

(6)套接字。这种类型用于进程间的网络通讯。套接字也可用于在一台宿主机上进程之间的非网络通讯  S_ISSOCK()

(7)符号连接。这种文件类型指向另外一个文件  S_ISLNK()

(文件类型信息包含在stat结构的st_mode成员中,如S_ISREG)

POSIX.1容许实现将进程间通讯(IPC)对象(例如,消息队列和信号量等)表示为文件。

<sys/stat.h>中的IPC类型宏:S_TYPEISMQ()  消息队列

               S_TYPEISSEM()  消息量

               S_TYPEISSHM()  共享存储对象

(这些宏的参数并不是st_mode,而是指向stat结构的指针)

实例:4_1 对每一个命令行参数打印文件类型

 1 #include"apue.h"
 2 int main(int argc,char *argv[])
 3 {
 4     int i;
 5     struct stat buf;
 6     char *ptr;
 7     
 8     for(i=1;i<argc;i++){
 9         printf("%s: ",argv[i]);
10         if(lstat(argv[i],&buf)<0){     //若改为stat(),则没法看到link类型
11             err_ret("lstat error");
12             continue;
13         }
14         if(S_ISREG(buf.st_mode))
15             ptr="regular";
16         else if (S_ISDIR(buf.st_mode))
17             ptr="directory";
18         else if (S_ISCHR(buf.st_mode))
19             ptr="character special";
20         else if (S_ISBLK(buf.st_mode))
21             ptr="block special";
22         else if (S_ISFIFO(buf.st_mode))
23             ptr="fifo";
24         else if (S_ISLNK(buf.st_mode))
25             ptr="symbolic link";
26         else if (S_ISSOCK(buf.st_mode))
27             ptr="socket";
28         else
29             ptr="** unknown mode **";
30         printf("%s\n",ptr);
31     }
32     exit(0);
33 }

 

4.4 设置用户ID和设置组ID

与一个进程相关联的ID有6个或更多:

-实际用户ID和实际组ID标识咱们到底是谁。这两个字段在登陆时取自口令文件中的登录项。一般,在一个登录会话间这些值并不改变,可是超级用户进程有方法改变它们

-有效用户ID,有效组ID以及附加组ID决定了咱们的文件访问全乡

-保存的设置用户ID和保存的设置组ID在执行一个程序时包含了有效用户ID和有效组ID的副本

一般,有效用户ID等于实际用户ID,有效组ID等于实际组ID。可是能够在文件模式字(st_mode)中设置一个特殊标志,其含义是”当执行此文件时,将进程的有效用户ID设置为文件全部者ID(st_uid)“(st_gid状况也相似)

例如,若文件的全部者是超级用户,并且设置了该文件的设置用户ID位,而后当该程序由一个进程执行时,则该进程具备超级用户特权。(无论执行此文件的进程的实际用户ID是什么,都进行这种处理)

 

4.5 文件访问权限

st_mode值也包含了针对文件的访问权限位,每一个文件有9个访问权限位,可将它们分红三类:owner  group  other

            S_IRUSR    用户-读

            S_IWUSR     用户-写

            S_IXUSR    用户-执行

            S_IRGRP    组-读

            S_IWGRP     组-写

            S_IXGRP    组-执行

            S_IROTH    其余-读

            S_IWOTH     其余-写

            S_IXOTH    其余-执行

注意:对于目录的读权限和执行权限的意义是不一样的。读权限容许咱们读目录,得到在该目录中全部文件名的列表。当一个目录是咱们要访问文件的路径名的一个组成部分时,对该        目录的执行权限使咱们能够经过该目录

访问位规则:

-咱们用名字打开任意类型的文件时,对该名字中包含的每个目录,包括它可能隐含的当前工做目录都应具备执行权限

-对于一个文件的读权限决定了咱们是否可以打开该文件进行读操做

-对于一个文件的写权限决定了咱们是够可以打开该文件进行写操做

-为了在open函数中对一个文件指定O_TRUNC标志,必须对该文件具备写权限

-为了在一个目录中建立一个新文件,必须对该目录具备写权限和执行权限

-为了删除一个现有文件,必须对包含该文件的目录具备写权限和执行权限。对该文件自己则不须要有读写权限

-若是6个exec函数中的任何一个执行某个文件,都必须对该文件具备执行权限。该文件还必须是一个普通文件

 

进程每次打开、建立或删除一个文件时,内核就进行文件访问权限测试:

(1)若进程的有效用户ID是0(超级用户),则容许访问。这给予了超级用户对整个文件系统进行处理的最充分的自由

(2)若进程的有效用户ID等于文件的全部者ID(也就是该进程拥有此文件),那么:若全部者适当的访问权限位被设置,则容许访问,否者拒绝访问。(适当的访问权限位指的是    若进程为读而打开该文件,则用户读位应为1;若进程为写而打开该文件,则用户写位应为1;若进程将执行该文件,则用户执行位应为1)

(3)若进程的有效组ID或进程的附加组ID之一等于文件的组ID,那么:若组适当的访问权限位被设置,则容许访问,否者拒绝访问。

(4)若其余用户适当的访问权限位被设置,则容许访问,否者拒绝访问

 

4.6 新文件和目录的全部权

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

(1)新文件的组ID能够是进程的有效组ID

(2)新文件的组ID能够是它所在目录的组ID

 

4.7 access函数

access函数是按实际用户ID和实际组ID进行文件访问权限测试的

#include<unistd.h>

int access(const char *pathname ,int mode);

                        返回值:若成功则返回0,若出错则返回-1

access函数的mode常量:

          R_OK  测试读权限

          W_OK  测试写权限

          X_OK  测试执行权限

          F_OK  测试文件是否存在

实例:4_2 access函数实例

 1 #include"apue.h"
 2 #include<fcntl.h>
 3 int main(int argc,char *argv[])
 4 {
 5     if(argc!=2)
 6     err_quit("usage: a.out <pathname>");
 7     if(access(argv[1],R_OK)<0)
 8     err_ret("access error for %s",argv[1]);
 9     else
10     printf("read access OK\n");
11     if(open(argv[1],O_RDONLY)<0)
12     err_ret("open error for %s",argv[1]);
13     else 
14      printf("open for reading OK\n");
15     exit(0);
16 }

4.8 umask函数

umask函数为进程设置文件模式建立屏蔽字,并返回之前的值

#include<sys/stat.h>

mode_t umask(mode_t cmask);

                            返回值:之前的文件模式建立屏蔽字

参数cmask是由st_mode中的9个常量(S_IRUSR,S_IWUSR等)中的若干个按位或构成的

 

在进程建立一个新文件或新目录时,就必定会使用文件模式建立屏蔽字

实例:4_3 umask函数实例

#include"apue.h"
#include<fcntl.h>

#define RWRWRW (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)

int main()
{
    umask(0);
    if(creat("foo",RWRWRW)<0)
        err_sys("creat error for foo");
    umask(S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
    if(creat("bar",RWRWRW)<0)
        err_sys("creat error for bar");
    exit(0);
}

此程序建立了两个文件,建立第一个时,umask值为0,建立第二个时,umask值禁止全部组和其余用户的访问权限。

UNIX系统的大多数用户从不处理他们的umask值,但当咱们的进程运行时,有效的umask值可能关闭该权限位,这时应该在进程运行时修改umask值

 

4.9 chmod 和fchmod 函数

这两个函数使咱们能够更改现有文件的访问权限

#include<sys/stat.h>

int chmod(const char *pathname,mode_t mode);    //在指定的文件上进行操做

int fchmod(int filedes,mode_t mode);      //对已打开的文件进行操做

                    两个函数返回值:若成功则放回0,若出错则返回-1

参数mode常量有9个与st_mode同样,不过多了6项,它们是两个设置ID常量(S_ISUID,S_ISGID),保存正文常量(S_ISVTX),以及三个组合常量(S_IRWXU,S_IRWXG,S_IRWXO)。

附:保存-正文位(S_ISVTX)不是POSIX.1的一部分。在SUS中,它被定义在XSI扩展中

实例:4_4 chmod 函数实例

 

 1 #include"apue.h"
 2 int main()
 3 {
 4     struct stat statbuf;
 5     
 6     /*turn on set-group-ID and turn off group-execute*/
 7     
 8     if(stat("foo",&statbuf)<0)
 9         err_sys("stat error for foo");
10     if(chmod("foo",(statbuf.st_mode&~S_IXGRP)|S_ISGID)<0)
11         err_sys("chmod error for foo");
12     
13     /*set absolute mode to "rw-r--r--"*/
14     if(chmod("bar",S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)<0)
15         err_sys("chmod error for bar");
16     exit(0);
17 }

(在运行程序4_4中的程序后,ls命令列出的时间和日期并无改变。在4.18节中,咱们会了解到chmod函数更新的只是i节点最近一次被更改的时间。按系统默认方式,ls -l 列出的是最后修改文件内容的时间)

 

4.10 粘住位(S_ISVTX)

若是对一个目录设置了粘住位,则只有对该目录具备写权限的用户在知足下列条件之一的状况下,才能删除或改名该目录下的文件:

-拥有此文件

-拥有此目录

-是超级用户

目录/tmp和/var/spool/uucppublic是设置粘住位的典型候选者------任何用户均可在这两个目录中建立文件。任意用户(用户、组和其余)对这两个目录的权限一般都是读、写和执行。可是用户不该能删除或改名属于其余人的文件,为此在这两个目录的文件模式中都设置了粘住位

 

4.11 chown,fchown和lchown函数

下面几个chown函数可用于更改文件的用户ID和组ID。

#include<unistd.h>

int chown(const char *pathname ,uid_t owner,gid_t group);

int fchown(int filedes,uid_t owner,gid_t group);

int lchown(const char *pathname,uid_t owner,gid_t group);

                    三个函数的返回值:若成功则返回0,若出错则返回-1

出了所引用的文件是符号连接外,这三个函数的操做类似。在符号连接的状况下,lchown更改符号连接自己的全部者,而不是该符号连接所指向的文件

 

_POSIX_CHOWN_RESTRICTED指明使用chown是不是受限的

 

若_POSIX_CHOWN_RESTRICTED对指定的文件起做用,则

(1)只有超级用户进程能更改该文件的用户ID

(2)若知足下列条件,一个非超级用户进程就能够更改该文件的组ID:

  a.进程拥有此文件(其有效用户ID等于该文件的用户ID)

  b.参数owner等于-1或文件的用户ID,而且参数group等于进程的有效ID或进程的附加组ID之一

这意味着,当_POSIX_CHOWEN_RESTRICTED起做用时,不能更改其余用户的用户ID。你能够更改你所拥有的文件的组ID,但只能改到你所属的组

 

 

4.12 文件长度

stat结构成员st_size表示以字节为单位的文件长度。此字段只对普通文件,目录文件和符号连接有意义

-对于普通文件,其文件长度能够是0,在读这种文件时,将获得文件结束(eof)指示

-对于目录,文件长度是一个数(例如16或512)的倍数

-对于符号连接,文件的长度是文件名中的实际字节数

 

文件中的空洞

当存在空洞时,磁盘总量小于文件长度,对于没有写过的字节位置,read函数读到的字节是0。

若是使用实用程序(例如cat)复制这种文件,那么全部这些空洞都会被填满,其中全部实际数据字节皆填写为0

$cat xxx > xxx.copy

新文件的实际字节数与文件长度接近,但并不相同。其缘由是文件系统使用了若干以存放指向实际数据块的各个指针。

 

4.13 文件截短

在文件尾端处截取一些数据以缩短文件(将一个文件清空为0是一个特例,在打开文件时使用O_TRUNC标志能够作到这一点

#include<unistd.h>

int truncate(const char *pathname, off_t length);

int ftruncate(int filedes, off_t length);
        
                    两个函数的返回值:若成功则返回0,若出错则返回-1

这两个函数把现有的文件长度截短为length字节。若是该文件之前的长度大于length,则超过部分不能再访问。若是之前的长度短于length,则其效果与系统有关(遵循XSI的系统将增长该文件的长度。若UNIX系统实现扩展了该文件,则在之前的文件尾端和新的文件尾端之间的数据将读做0(也就是可能在文件中建立了一个空洞))

 

4.14 文件系统

http://www.cnblogs.com/qianye/archive/2012/11/24/2786345.html

 

4.15 link,unlink,remove和rename函数

任何一个文件能够有多个目录项指向其i节点。建立一个指向现有文件的连接的方法是使用link函数

#include<unistd.h>

int link(const char *existingpath, const char *newpath);
    
                            返回值:若成功则返回0,若出错则返回-1

此函数建立一个新目录项newpath,它引用现有的文件existingpath。如若newpath存在,则返回出错。

 

为了删除一个现有的目录项,能够调用unlink函数

#include<unistd.h>

int unlink(const char *pathname);

                    返回值:若成功则返回0,若出错则返回-1

此函数删除目录项,并将由pathname所引用文件的连接计数减1。若是还有指向该文件的其余连接,则仍可经过其余连接访问该文件的数据。若是出错,则不对该文件作任何修改

(只有当连接计数达到0时,该文件内容才可被删除。另外一个条件也会阻止删除文件的内容------只要有进程打开了该文件,其内容也不能删除。关闭一个文件时,内核首先检查打开该文件的进程数。若是该数达到0,而后内核检查其连接数,若是这个数也是0,那么就删除该文件的内容)

实例:4_5 打开一个文件,而后unlink它

 1 #include"apue.h"
 2 #include<fcntl.h>
 3 
 4 int main()
 5 {
 6     if(open("tempfile",O_RDWR)<0)
 7         err_sys("open error");
 8     if(unlink("tempfile")<0)
 9         err_sys("unlink error");
10     printf("file unlinked\n");
11     sleep(15);
12     printf("done\n");
13     exit(0);
14 }

进程用open建立一个文件,而后当即调用unlink。由于该文件仍旧是打开的,因此不会将其内容删除。只有当进程关闭该文件或终止时,该文件的内容才会被删除。

若是pathname是符号连接,那么unlink删除该符号连接,而不会删除由该连接所引用的文件,给出符号连接名的状况下,没有一个函数能删除由该连接所引用的文件

 

remove函数能够解除对一个文件或目录的连接。(对于文件,remove的功能与unlink相同。对于目录,remove的功能与rmdir相同)

#include<stdio.h>

int remove(const char *pathname);

                        返回值:若成功则返回0,若出错则返回-1

 

 

文件或目录用rename函数改名

#include<stdio.h>

int rename(const char *oldname, const char *newname);

                        返回值:若成功则返回0,若出错则返回-1
根据oldname是文件仍是目录,分为如下4种状况:(还没弄明白)
1,若是oldname指的是一个文件而不是目录,那么为该文件或符号连接改名。在这种状况下,若是newname已存在,则它不能引用一个目录。若是newname已存在,并且不是一个目录,则先将该目录删除而后将oldname改名为newname。对于包含oldname的目录以及包含newname的目录,调用进程必须具备写权限,由于将更改这两个目录;
2,若oldname指的是一个目录,那么为该目录改名。若是newname已存在,则它必须引用一个目录,并且该目录应当是空目录(空目录指的是该目录中只有.和..项)。若是newname已存在并且是一个空目录,则现将其删除,而后将oldname改名为newname。另外,当为一个目录改名时,newname不能包含oldname做为其路径前缀,由于旧名字是新名字的前缀而不能将其删除;
3,若oldname或newname引用符号连接,则处理的是符号连接自己,而不是它所引用的文件;
4,做为特例,若oldname和newname引用同一文件,则函数不作任何更改而成功返回。


4.16 符号连接

符号连接是指向一个文件的指针。引入符号连接的缘由是为了避开硬连接的一些限制:

-硬连接一般要求连接和文件位于同一文件系统中

-只有超级用户才能建立指向目录的硬连接

对符号连接以及它指向何种对象并没有任何文件系统限制,任何用户均可建立指向目录的符号连接。符号连接通常用于将一个文件或整个目录结构移到系统中的另外一个位置

 

当使用以名字引用文件的函数时,应当了解该函数是否处理符号连接。也就是该函数是否跟随符号连接到达它所连接的文件。如若该函数具备处理符号连接的功能,则其路径名参数引用由符号连接指向的文件。不然,路径名参数将引用连接自己,而不是该连接指向的文件。(mkdir,mkinfo,mkmod和rmdir这些函数,当路径名是符号连接时,都返回出错)

附各个函数对符号连接的处理:http://blog.chinaunix.net/uid-15084954-id-190319.html

注:同时用O_CREAT和O_EXCL二者调用open函数。在此状况下,若路径名引用符号连接,open将返回出错,并将errno设置为EEXIST。这种处理方式的意图是堵塞一个安全性漏洞,使具备特权的进程不会被诱骗对不适当的文件进行写操做

实例:使用符号连接可能在文件系统中引入循环

mkdir foo
touch foo/a
ln -s ../foo foo/testdir
ls -l foo


用符号连接指定文件时,即便该文件不存在,也能创建连接,但若用open函数指定此连接,则返回出错,由于不存在该文件。

 

4.17 symlink 和 readlink函数

symlink函数建立一个符号连接

#include<unistd.h>

int symlink(const char *actualpath, const char *sympath);

                    返回值:若成功则返回0,若出错则返回-1

该函数建立了一个指向actualpath的新目录项sympath,在建立此符号连接时,并不要求actualpath已经存在。而且actualpath和sympath并不须要位于同一文件系统

 

由于open函数跟随符号连接,因此须要有一种方法打开连接自己,并读该连接中的名字。readlink函数提供了这种功能

#include<unistd.h>

ssize_t readlink(const char* restrict pathname, char *restrict buf,size_t bufsize);

                    返回值:若成功则返回读到的字节数,若出错则返回-1

此函数组合了open,read和close的全部操做。若是此函数成功执行,则它返回读入buf的字节数。在buf中返回的符号连接的内容不以null字符终止

 

4.18 文件的时间

st_atime,st_mtime,st_ctime

修改时间是文件内容最后一次被修改的时间。更改状态时间是该文件的i节点最后一次被修改的时间。

影响i节点的操做:更改文件的访问权限,用户ID,连接数等,但它们并无更改文件的实际内容(系统并不保存对一个i节点的最后一次访问时间,因此access和stat函数并不更改这         三个时间中的任何一个)

ls命令按这三个时间值中的一个排序进行显示。按系统默认(用-l或-t选项时),它按文件的修改时间的前后排序显示,-u选项使其用访问时间排序,-c选项则使其用更改状态时间排序

 

4.19 utime函数

一个文件的访问和修改时间能够用utime函数更改

#include<utime.h>

int utime(const char *pathname, const struct utimbuf *times);

                            返回值:若成功则返回0,若出错则返回-1

此函数所用的数据结构是

struct utimbuf{
        time_t actime;
        time_t modtime;
};

此结构中的两个时间值是日历时间。这是自1970年1月1日00:00:00以来国际标准时间所通过的秒数

此函数的操做以及执行它所要求的特权取决于times参数是不是null:

-若是times是一个空指针,则访问时间和修改时间二者都设置为当前时间。为了执行此操做必须知足下列条件之一:进程的有效用户ID必须等于该文件的全部者ID;或者进程对该文件必须具备写权限

-若是times是非空指针,则访问时间和修改时间被设置为times所指向结构中的值。此时,进程的有效用户ID必须等于该文件的全部者ID,或者进程必须是一个超级用户进程。对文件只具备写权限是不够的

 

程序清单:4_6 utime函数实例

 1 #include"apue.h"
 2 #include<fcntl.h>
 3 #include<utime.h>
 4 
 5 int main(int argc,char *argv[])
 6 {
 7     int i,fd;
 8     struct stat statbuf;
 9     struct utimbuf    timebuf;
10     for(i=1;i<argc;i++){
11         if(stat(argv[i],&statbuf)<0){
12             err_ret("%s:stat error",argv[i]);
13             continue;
14         }
15         if((fd=open(argv[i],O_RDWR|O_TRUNC))<0){
16             err_ret("%s:open error",argv[i]);
17             continue;
18         }
19         close(fd);
20         timebuf.actime=statbuf.st_atime;
21         timebuf.modtime=statbuf.st_mtime;
22         if(utime(argv[i],&timebuf)<0){
23             err_ret("%s:utime error",argv[i]);
24             continue;
25         }
26     }
27     exit(0);
28 }

运行后,最后修改时间和最后访问时间未变。可是,更改状态时间则更改成程序运行时的时间

 

4.20  mkdir 和 rmdir函数

用mkdir函数建立目录,用rmdir函数删除目录

#include<sys/stat.h>

int mkdir(const char *pathname, mode_t mode);

                    返回值:若成功则返回0,若出错则返回-1

 

用rmdir函数能够删除一个空目录。空目录是只包含.和..这两项的目录

#include<unistd.h>

int rmdir(const char *pathname);

                    返回值:若成功则返回0,若出错则返回-1

若是调用此函数使目录的连接计数成为0,而且也没有其余进程打开此目录,则释放由此目录占用的空间。若是在连接计数达到0时,有一个或几个进程打开了此目录,则在此函数返回前删除最后一个连接及.和..项。另外,在此目录中不能在建立新文件。可是在最后一个进程关闭它前并不释放此目录。(即便另外一个进程打开该目录,他们在此目录下也不能执行其余操做。这样处理的缘由是,为了使rmdir函数成功执行,该目录必须是空的)

 

4.21 读目录

对某个目录具备访问权限的任一用户均可读该目录,可是,为了防止文件系统产生混乱,只有内核才能写目录。一个目录的写权限位和执行权限位决定了在该目录中可否建立新文件以及删除文件,他们并不表示可否写目录(不太明白)

#include<dirent.h>

DIR *opendir(const char *pathname);
                    //返回值:若成功则返回指针,出错则返回null

struct dirent *readdir(DIR *dp);
                    //返回值:若成功则返回指针,若在目录结尾或出错则返回null

void rewinddir(DIR *dp);

int closedir(DIR *dp);
                                   //返回值:若成功则返回0,若出错则返回-1
long telldir(DIR *dp);
                                //返回值:与dp关联的目录中的当前位置
void seekdir(DIR *dp, long loc);

 

struct dirent{
        ino_t d_ino;    //i-node number
        char d_name[NAME_MAX+1];  //null-terminated filename
}

 

简单的文件遍历程序

实例:4_7 递归降序遍历目录层次结构,并按文件类型计数

  1 #include"apue.h"
  2 #include<dirent.h>
  3 #include<limits.h>
  4 
  5 /*function type that is called for each filename*/
  6 typedef int Myfunc(const char *,const struct stat *,int);
  7 static Myfunc myfunc;
  8 static int myftw(char *,Myfunc *);
  9 static int dopath(Myfunc *);
 10 
 11 static long nreg,ndir,nblk,nchr,nfifo,nslink,nsock,ntot;
 12 
 13 int main(int argc,char *argv[])
 14 {
 15     int ret;
 16     if(argc!=2)
 17         err_quit("usage: ftw <starting-pathname>");
 18     ret = myftw(argv[1],myfunc);    //does it all
 19     ntot=nreg+ndir+nblk+nchr+nfifo+nslink+nsock;
 20     
 21     if(ntot==0)
 22         ntot=1; //avoid divide by 0;print 0 for all counts
 23     printf("regular files =%7ld,%5.2f %%\n",nreg,nreg*100.0/ntot);
 24     printf("directories = %7ld,%5.2f %%\n",ndir,ndir*100.0/ntot);
 25     printf("block special =%7ld,%5.2f %%\n",nblk,nblk*100.0/ntot);
 26     printf("char special =%7ld,%5.2f %%\n",nchr,nchr*100.0/ntot);
 27     printf("FIFOs =%7ld,%5.2f %%\n",nfifo,nfifo*100.0/ntot);
 28     printf("symbloic links=%7ld,%5.2f %%\n",nslink,nslink*100.0/ntot);
 29     printf("sockets =%7ld,%5.2f %%\n",nsock,nsock*100.0/ntot);
 30     exit(ret);
 31 }
 32 /*
 33  *Descend through the hierarchy,starting at "pathname".
 34  *The caller's func() is called for every file
 35  */
 36 #define FTW_F 1 //file other than directory
 37 #define FTW_D 2 //directory
 38 #define FTW_DNR 3 //directory that can't be read
 39 #define FTW_NS 4 //file that we can't stat
 40 
 41 static char *fullpath; //contains full pathname for every file
 42 
 43 static int myftw(char *pathname,Myfunc *func)
 44 {
 45     int len;
 46     fullpath=path_alloc(&len);    //malloc's for PATH_MAX+1 bytes
 47     strncpy(fullpath,pathname,len); //protect against
 48     fullpath[len-1]=0;    //buffer overrun
 49     return(dopath(func));
 50 }
 51 /*
 52  *Descend through the hierarchy,starting at "fullpath"
 53  *If "fullpath" is anything other than a directory,we lstat() it,
 54  *call func(),and return . For a directory,we call ourself
 55  *recursively for each name in the directory
 56  */
 57 static int dopath(Myfunc* func)   //we return whatever func() returns
 58 {
 59     struct stat statbuf;
 60     struct dirent *dirp;
 61     DIR *dp;
 62     int ret;
 63     char *ptr;
 64     if(lstat(fullpath,&statbuf)<0) //stat error
 65         return(func(fullpath,&statbuf,FTW_NS));
 66     if(S_ISDIR(statbuf.st_mode)==0) //not a directory
 67         return(func(fullpath,&statbuf,FTW_F));
 68     /*
 69      *It's a directory.First call func() for the directory
 70      *then process each filename in the directory.
 71      */
 72     if((ret=func(fullpath,&statbuf,FTW_D))!=0)
 73         return(ret);
 74     ptr=fullpath+strlen(fullpath);//point to end of fullpath
 75     *ptr++='/';
 76     *ptr=0;
 77     if((dp=opendir(fullpath))==NULL)//can't read directory
 78         return(func(fullpath,&statbuf,FTW_DNR));
 79     while((dirp=readdir(dp))!=NULL){
 80         if(strcmp(dirp->d_name,".")==0||strcmp(dirp->d_name,"..")==0)
 81             continue;//ignore dot and dot-dot
 82         strcpy(ptr,dirp->d_name);//append name after slash
 83         if((ret=dopath(func))!=0)//recursive
 84             break;
 85     }
 86     ptr[-1]=0;//erase everthing for alash onwards
 87     if(closedir(dp)<0)
 88         err_ret("can't close directory %s",fullpath);
 89     return(ret);
 90 }
 91 char* path_alloc(int*size)
 92 {
 93     char *p=NULL;
 94     if(!size)
 95     return NULL;
 96     p=malloc(256);
 97     if(p)
 98     *size=256;
 99     else
100     *size=0;
101     return p;
102 }
103 static int myfunc(const char *pathname,const struct stat *statptr,int type)
104 {
105     switch(type){
106     case FTW_F:
107         switch(statptr->st_mode &S_IFMT){
108         case S_IFREG:    nreg++; break;
109         case S_IFBLK:    nblk++; break;
110         case S_IFCHR:    nchr++; break;
111         case S_IFIFO:    nfifo++;break;
112         case S_IFLNK:    nslink++;break;
113         case S_IFSOCK:  nsock++;break;
114         case S_IFDIR:
115             err_dump("for S_IFDIR for %s",pathname);
116                 //directories should have type = FTW_D
117         }
118         break;
119     case FTW_D:
120         ndir++;
121         break;
122     case FTW_DNR:
123         err_ret("can't read directory %s",pathname);
124         break;
125     case FTW_NS:
126         err_ret("stat error for %s",pathname);
127         break;
128     default:
129         err_dump("unknown type %d for pathname %s",type,pathname);
130     }
131     return(0);
132 }
View Code


按书上给的函数编译后会告知path_alloc()未定义,解决办法:http://www.cnblogs.com/xiaoliyu/archive/2009/03/08/1406448.html

 

4.22 chdir,fchdir和getcwd函数

进程经过调用chdir或fchdir函数能够更改当前工做目录

#include<unistd.h>

int chdir(const char *pathname);      //$ cd

int fchdir(int fieldes);

    
                两个函数的返回值:若成功则返回0,若出错则返回-1

在这两个函数中,分别用pathname或打开文件描述符

 

实例:4_8 chdir函数实例

1 #include"apue.h" 
2 int main()
3 {
4     if(chdir("/tmp")<0)
5         err_sys("chdir failed");
6     printf("chdir to /tmp succeeded\n");
7     exit(0);
8 }

执行完这段程序后能够看出,当前工做目录没有改变,其缘由是shell建立了一个子进程,由该子进程具体执行mycd程序。因而可知,为了改变shell进程本身的工做目录,shell应当直接调用chdir函数,为此cd命令的执行程序直接包含在shell程序中

 

getcwd获取当前目录路径名

#include<unistd.h>

char *getcwd(char *buf,size_t size);

                    返回值:若成功则返回buf,若出错则返回NULL

 

4.23 设备特殊文件

st_dev和st_rdev这两个字段常常引发混淆:

-每一个文件系统所在的存储设备都由其主、次设备号表示。设备号所用的数据类型是基本系统数据类型dev_t。主设备号标识设备驱动程序,有时编码为其通讯的外设板;次设备号标识特定的子设备

-咱们一般可使用两个宏即major和minor来访问主、次设备号,大多数实现都定义了这两个宏。这就意味着咱们无需关心这两个数是如何存放在dev_t对象中的。

-系统中与每一个文件名关联的st_dev值是文件系统的设备号,该文件系统包含了这一文件以及与其对应的i节点

-只有字符特殊文件和块特殊文件才有st_rdev值,此值包含实际设备的设备号

 

实例:4_10 打印st_dev和st_rdev值

 1 #define _BSD_SOURCE
 2 #include"apue.h"
 3 //#ifdev SOLARIS
 4 //#include<mkdev.h>
 5 //#endif
 6 
 7 int main(int argc,char *argv[])
 8 {
 9     int i;
10     struct stat buf;
11     for(i=1;i<argc;i++){
12     printf("%s: ",argv[i]);
13     if(stat(argv[i],&buf)<0){
14         err_ret("stat error");
15         continue;
16     }
17     printf("dev = %d/%d",major(buf.st_dev),minor(buf.st_dev));
18     if(S_ISCHR(buf.st_mode)||S_ISBLK(buf.st_mode)){
19     printf(" (%s) rdev=%d/%d",(S_ISCHR(buf.st_mode))?"character":"block",
20         major(buf.st_rdev),minor(buf.st_rdev));
21     }
22     printf("\n");
23     }
24     exit(0);
25 }

此程序按书上编译时找不到<sys/mkdev.h>,后来直接在第一行加上一句就行

 

4.24 文件访问权限位小结

 

常量 说明 对普通文件的影响 对目录的影响
S_ISUID
S_ISGID
设置用户ID
设置组ID
执行时设置有效用户ID
若组执行位设置,则执行时设置有效组ID,不然使强制性记录锁起做用(若支持)
(不使用)
将在目录中建立的新文件的组ID设置为目录的组ID
S_ISVTX 粘住位 在交换区保存程序正文(若支持) 限制在目录中删除和改名文件
S_IRUSR
S_IWUSR
S_IXUSR
用户读
用户写
用户执行
许可用户读文件
许可用户写文件
许可用户执行文件
许可用户读目录项
许可用户在目录中删除和建立文件
许可用户在目录中搜索给定路径名
S_IRGRP
S_IWGRP
S_IXGRP
组读
组写
组执行
许可组读文件
许可组写文件
许可组执行文件
许可组读目录项
许可组在目录中删除和建立文件
许可组在目录中搜索给定路径名
S_IROTH
S_IWOTH
S_IXOTH
其余读
其余写
其余执行
许可其余读文件
许可其余写文件
许可其余执行文件
许可其余读目录项
许可其余在目录中删除和建立文件
许可其余在目录中搜索给定路径名

 

最后9个常量分红3组,由于:

S_IRWXU = S_IRUSR | S_IWUSR | S_IXUSR

S_IRWXG = S_IRGRP | S_IWGRP | S_IXGRP

S_IRWXO = S_IROTH | S_IWOTH | S_IXOTH

相关文章
相关标签/搜索