首页 > 代码库 > UNIX_文件&目录

UNIX_文件&目录

除了对文件进行打开,读写等操作。文件系统还有其他的特征和性质等着我们去研究哦。

stat、fstat、lstat函数

#include <sys/stat.h>int stat(const char *restrice pathname, struct stat *restrict buf);int fstat(int fieldes, struct stat *buf);int lstat(const char *restrice pathname, struct stat *restrict buf);

成功,返回0;否则,返回-1。

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

第二个参数,buf是指针,指向一个结构。结构的基本形式如下:

struct stat {

  mode_t   st_mode;  /* file type & mode (permissions) */

  ino_t   st_ino;  /* i-node number (serial number) */

  dev_t   st_dev;   /* device number (file system) */

  dev_t   st_rdev;   /* device number for special files */

  nlink_t   st_nlink;   /* 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   t_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 */

};

程序:对每个命令行参数打印文件类型

#include "apue.h"intmain(int argc, char *argv[]){    int            i;    struct stat    buf;    char        *ptr;    for (i = 1; i < argc; i++) {        printf("%s: ", argv[i]);        if (lstat(argv[i], &buf) < 0) {            err_ret("lstat error");            continue;        }        if (S_ISREG(buf.st_mode))            ptr = "regular";        else if (S_ISDIR(buf.st_mode))            ptr = "directory";        else if (S_ISCHR(buf.st_mode))            ptr = "character special";        else if (S_ISBLK(buf.st_mode))            ptr = "block special";        else if (S_ISFIFO(buf.st_mode))            ptr = "fifo";        else if (S_ISLNK(buf.st_mode))            ptr = "symbolic link";        else if (S_ISSOCK(buf.st_mode))            ptr = "socket";        else            ptr = "** unknown mode **";        printf("%s\n", ptr);    }    exit(0);}

文件访问权限

9个访问权限位,可看做三类,u表示用户(所有者user),g表示组(group),o表示其他(other)。每个类中三个访问权限(即读(R),写(W),执行(X))。

进程是根据有效用户ID判断当前用户是否有无权限的。进程每次打开,创建,删除一个文件时,内核就进行文件访问权限测试。 超级用户的有效ID是0。

如果进程拥有此文件,则按照用户访问权限批准或拒绝该进程对文件的访问(不查看组访问权限);若不拥有该文件,但进程属于某个适当的组,则按照组访问权限批准或拒绝该进程对文件的访问。

 access函数

#include <unistd.h>int access(const char* pathname, int mode);

成功,返回0;失败,返回-1。

该函数按实际用户ID和实际组ID进行访问权限测试,也就是想验证一个进程的实际用户能否访问一个给定的文件。其中mode是如下所列常量的按位或。

R_OK 测试读权限

W_OK 测试写权限

X_OK 测试执行权限

F_OK 测试文件是否存在

umask函数

mode_t  umask(mode_t  cmask)

cmask是由9个访问权限位(S_IRUSR, S_IWUSR,...)中的若干个按位“或”构成的。

该函数为进程设置文件模式创建屏蔽字,也就是给一个文件设置权限,比如:禁止掉组和其他用户的读写权限等, 并返回以前的值。该函数无出错返回。

#include "apue.h"#include <fcntl.h>#define RWRWRW (S_IRUSR) | S_IWUSR | S_IRGRP | S_IROTH | S_IWOTH)int main(){    umask(0); //创建第一个文件,umask为0    if(creat("foo", RWRWRW) < 0)        err_sys("creat error for foo");    umask(S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); //创建第二个,umask值禁止所有组和其他用户的读写访问权    if(creat("bar", RWRWRW) < 0)        err_sys("creat error for bar");    exit(0);}

上面的程序创建两个文件,创建第一个时,umask为0,这是任何用户都可以读文件;创建第二个时,umask值禁止所有组和其他用户的读写访问权限。

umask值是一个八进制数,每一位都代表一种要屏蔽的权限(u=rwx, g=rwx, o=rwx)。设置了相应位之后,它所对应的权限就会被拒绝。常用的是002(其他用户写),022(同组成员和其他用户写),027(同组成员写和其他用户读、写或执行)。

Single UNIX Specification要求shell支持符号形式的umask命令。该格式与八进制格式刚好相反,即设置了的相应位表示非拒绝。

chmod、fchmod函数

这两个函数用于更改现有文件的访问权限。

#include <sys/stat.h>int chmod(const char *pathname, mode_t mode);int fchmod(int fieldes, mode_t mode);

成功,返回0;失败,返回-1。

chmod在指定的文件上操作,fchmod则是对已经打开的文件操作。

S_ISVTX(sticky bit)  :保存文本(粘住位)。用于目录,使多个用户可以共享一个目录而不会彼此影响。只有超级用户才可以设置粘住位。现在不需要这种技术了。

 

文件系统

硬链接与软链接(符号链接)

硬链接:每个i节点都有一个链接计数,其值是指向该i节点的目录项数。只有当链接计数为0时,才可以删除该文件,所以删除文件用unlink, 不是delete。在stat结构中,连接计数包含在st_nlink中。这种链接叫硬链接。硬链接说白了是一个指针,指向文件i节点。

符号链接:是指向一个文件的间接指针。建立软链接就是建立了一个新文件。 软链接文件有点类似于Windows的快捷方式。它实际上是特殊文件的一种。在符号连接中,文件实际上是一个文本文件,其中包含的有另一文件的位置信息,可以是任意文件或目录,可以链接不同文件系统的文件。

引入符号链接是为了避开硬链接的限制:

1.硬链接通常要求链接和文件位于同一个文件系统中。

2.只有超级用户才能创建指向目录的硬链接。因为这样做可能在文件系统中形成循环。软连接中也能形成循环,但是在软链接中这种循环用unlink就很好消除。

如何形成循环的?如下:

$mkdir  foo  //创建目录$touch  foo/a  //创建0长文件$ln  -s  ../foo  foo/testdir  //创建符号链接$ls   -l   foo

这里创建了一个目录foo,它包含一个名字为a的文件以及一个指向foo的符号链接。

下图中,圆表示目录,方表示文件。使用Solaris的标准函数ftw(3)以降序遍历文件结构,则打印出的路径名如下:

 因为unlink不跟随软连接,所以可以unlink文件foo/testdir,从而消除这个循环。

什么叫不跟随软连接呢?

当使用以名字引用文件的函数时,我们应当了解函数是否跟随这个软链接到达它所链接的文件,也可以说是否处理符号链接,如果有这种功能,则其路径名参数就引用这个软连接指向的文件;否则就只是引用这个链接本身(就是个路径名)。

link、symlink、readlink函数

#include <unistd.h>int link(const char *existingpath, const char *newpath);int unlink(const char *pathname)

 link产生硬链接,即链接文件与目标文件是等价的,只是名字不同。目标文件必须存在。通常目标文件不允许是目录,也不允许跨越文件系统。成功,返回0;失败,返回-1.

#include <unistd.h>int symlink(const char *actualpath, const char *sympath);ssize_t readlink(const char* restrict pathname, char *restrict buf);

symlink产生软链接,即链接文件是将目标文件名作为字符序列存储起来,因此对目标文件没有任何限制。可以用readlink读取符号链接所存储的目标文件名.成功,返回0;失败,返回-1.

读目录

 

DIR *opendir(const char *pathname);  // 返回值:NULL表示出错,非空指针指向的对象为打开的目录数据结构(目录流)。

struct dirent *readdir(DIR *dp);  // 返回值: NULL表示读到目录尾部或出错,非空指针指向的对象为读取的目录项结构,目录流的内部指针指向下一个目录项。

 一个dirent结构体至少包含下列两个成员:

struct   dirent {    /*目录项结构 */

  ino_t   d_ino;   /* i-结点号 */

  char   d_name[NAME_MAX + 1]; /* null结尾的文件名 */

 }

void rewinddir(DIR *dp);   // 使readdir从目录的开头读(即目录流的内部指针指向第一个目录项)。

int closedir(DIR *dp);  // 返回值: 0 if OK, 1 on error.

 

DIR是一个内部结构,上述6个函数用这个内部结构保存当前正在被读的目录所在的有关信息。

opendir返回的指向DIR结构的指针由另外的5个函数使用。

只包含 ... 的目录为一个空目录。

一个递归降序遍历目录层次结构,并按照文件类型计数的程序如下:

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

 

chdir、fchdir、getcwd函数

每个进程都有一个当前工作目录,此目录是搜索所有相对路径的起点(不以斜杠开始的路径为相对路径)。

通过chdir和fchdir函数可以更改当前工作路径。

#include <unistd.h>int chdir(const char *pathname);int fchdir(int filedes);

成功,返回0;失败,返回-1.

But,内核为每个进程只保存指向该目录v节点的指针等目录本身的信息,并不保存该目录的完整路径名。

Luckily,getcwd函数则提供了得到当前工作目录完整的绝对路径名的功能。成功,返回buf; 失败,返回NULL。

#include <unistd.h>char *getcwd(char *buf, size_t  size);

第一个参数是缓冲地址buf, 第二是缓冲的长度size(单位:字节)。该缓冲必须有足够长度以容纳绝对路径名再加上一个null终止字符,否则返回出错。

 1 #include "apue.h" 2  3 int 4 main(void) 5 { 6     char    *ptr; 7     size_t        size; 8  9     if (chdir("/usr/spool/uucppublic") < 0)10         err_sys("chdir failed");11 12     ptr = path_alloc(&size);    /* our own function */13     if (getcwd(ptr, size) == NULL)14         err_sys("getcwd failed");15 16     printf("cwd = %s\n", ptr);17     exit(0);18 }

在更换目录之前我们可以先调用getcwd函数获得路径名,然后将这个路径名作为调用参数传送给chdir.

fchdir函数更便捷。在更换到文件系统的不同位置之前,使用open函数打开当前工作目录,然后保存文件描述符,当希望回到原工作目录时,只要将这个文件描述符传递给fchdir即可。

UNIX_文件&目录