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"

int
main(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
", 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 %%
", nreg,
 28       nreg*100.0/ntot);
 29     printf("directories    = %7ld, %5.2f %%
", ndir,
 30       ndir*100.0/ntot);
 31     printf("block special  = %7ld, %5.2f %%
", nblk,
 32       nblk*100.0/ntot);
 33     printf("char special   = %7ld, %5.2f %%
", nchr,
 34       nchr*100.0/ntot);
 35     printf("FIFOs          = %7ld, %5.2f %%
", nfifo,
 36       nfifo*100.0/ntot);
 37     printf("symbolic links = %7ld, %5.2f %%
", nslink,
 38       nslink*100.0/ntot);
 39     printf("sockets        = %7ld, %5.2f %%
", 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 int
124 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
", ptr);
17     exit(0);
18 }

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

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

原文地址:https://www.cnblogs.com/beatrice7/p/4072167.html