除了对文件进行打开,读写等操作。文件系统还有其他的特征和性质等着我们去研究哦。
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个函数使用。
只包含 .和.. 的目录为一个空目录。
一个递归降序遍历目录层次结构,并按照文件类型计数的程序如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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 }
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即可。