4.2 stat fstat lstat函数
#include <sys/stat.h>
int stat(const char * restrict pathname,struct stat * restrict buf);
int fstat(int fileds,struct stat * buf);
int lstat(const char * restrict pathname,struct stat *restrict buf);
三个函数的返回值:若成功则返回0 若出错则返回-1
stat结构体:
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 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 */
};
说明:st_mode:文件权限, 文件类型,设置用户ID,设置用户组ID都在里边。
<sys/stat.h>
Macro Type of file
S_ISREG() regular file
S_ISDIR() directory file
S_ISCHR() character special file
S_ISBLK() block special file
S_ISFIFO() pipe or FIFO
S_ISLNK() symbolic link
S_ISSOCK() socket
<sys/stat.h>
Macro Type of object
S_TYPEISMQ() message queue
S_TYPEISSEM() semaphore
S_TYPEISSHM() shared memory object
eg:测试文件类型:
#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\n", ptr);
}
exit(0);
}
4.4 设置用户ID和设置组ID
real user ID | who we really are |
effective user ID | used for file access permission checks |
saved set-user-ID | saved by exec functions |
实际用户ID和实际组ID标识我们究竟是谁。这两个字段在登录时取自口令文件中的登录项
有效用户ID,有效组ID及附加组ID决定了我们的文件访问权限。
保存的设置用户ID和保存的设置组ID在执行一个程序时包含了有效用户ID和有效组ID的副本
每个文件都有一个所有者和组所有者,所有者由stat结构中的st_uid成员表示,组成原者则由st_gid成员表示。
设置用户ID位及设置组ID位都包含在st_mode值中。
如果设置了用户ID位则表示当执行此文件时将进程的有效用户ID设置为文件所有者的用户ID(st_uid),设置了组ID位以此类推。
4.5 文件访问权限
st_mode mask | Meaning |
---|---|
S_IRUSR | user-read |
S_IWUSR | user-write |
S_IXUSR | user-execute |
S_IRGRP | group-read |
S_IWGRP | group-write |
S_IXGRP | group-execute |
S_IROTH | other-read |
S_IWOTH | other-write |
S_IXOTH | other-execute |
内核进行权限的测试是:
1.若进程的有效用户ID是0,则允许访问。
2.若进程的有效用户ID等于文件的所有者ID,那么若所有者适当的访问权限位被设置,则允许访问,否则拒绝访问。
3.若进程的有效组ID或进程的附加组ID之一等于文件的组ID,那么:若组适当的访问权限位被设置,则允许访问,否则拒绝访问。
4.若其他用户适当的访问权限位被设置,则允许访问, 否则拒绝访问。
补充:对linux 一般来说:如果它所在的目录设置了组ID,则新文件的组ID设置为目录的组ID,否则将新文件的组ID设置为进程的有效组ID。
4.6 新文件和目录的所有权
新文件的用户ID设置为进程的有效用户ID,关于组ID,分下列两种情况:
1.新文件的组ID可以是进程的有效组ID。
2.新文件的组ID可以是它所在目录的组ID。
4.7 access函数
access函数是按实际用户ID和实际组ID进行访问权限测试的。而不管setUID, 和 setGID
#include <unistd.h>
int access(const char * pathname, in mode); 返回值:若成功则返回0, 若出错则返回-1
mode | Description |
---|---|
R_OK | test for read permission |
W_OK | test for write permission |
X_OK | test for execute permission |
F_OK | test for existence of file |
eg:
#include "apue.h" #include <fcntl.h> int main(int argc, char *argv[]) { if (argc != 2) err_quit("usage: a.out <pathname>"); if (access(argv[1], R_OK) < 0) err_ret("access error for %s", argv[1]); else printf("read access OK\n"); if (open(argv[1], O_RDONLY) < 0) err_ret("open error for %s", argv[1]); else printf("open for reading OK\n"); exit(0); }
4.8 umask函数
#include <sys/stat.h>
mode_t umask(mode_t cmask);
eg:umask函数的例子
#include "apue.h"
#include <fcntl.h>
#define RWRWRW (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
int main(void)
{
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);
}
说明:f00 RWRWRW bar rw-------,create受umask的影响!!!
4.9 chmod fchmod函数
#include <sys/stat.h>
int chmod (const char * pathname, mode_t mode);
int fchmod (int fileds, mode_t mode); 两个函数的返回值,若成功则返回0,若出错则返回-1
说明:为了改变一个文件的权限位, 进程的有效用户ID必须等于文件的所有者ID,或者该进程必须具有超级用户权限。
4.10粘帖位
如果对一个目录设置了粘住位,则只有对该目录具有写权限的用户在满足下列条件之一的情况下,才能删除或更名该目录下的文件:
1.拥有此文件 2.拥有此目录 3.超级用户
4.10 chown fchown lchown函数
#include <unistd.h>
int chown (const char * pathname, uid_t owner, gid_t group);
int fchown(int fileds, uid_t owner, gid_t group);
int lcown (const char * pathname, uid_t owner, gid_t group); 三个函数 成功返回0 ,出错返回-1
如果这些函数由非超级用户进程调用,则在成功返回时,该文件的设置用户ID位和设置组ID位都会被清除
4.12 文件长度
stat结构成员st_size表示以字节为单位的文件长度。此字段只对普通文件,目录文件和符号链接有意义。
对于普通文件,其文件长度可以是0,再读这种文件时,将得到文件结束标志
对于目录,文件长度通常是一个数(例如 16 或512 )的倍数.
对于符号链接,文件长度是文件名中的实际字节数。(因为符号链接文件长度是由st_size指示,所以它并不包含通常C语言用作名字结尾的null字符)
文件空洞:如果使用实用程序复制这种文件,那么所有这些空洞都会被填满,其中所有实际数据字节皆填为0.
4.13文件截短
#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 文件系统
1.在stat结构中,链接计数包含在st_nlink成员中,其基本系统数据类型是nlink_t,这种链接类型成为硬链接。
2.I节点包含了大多数与文件有关的信息: 文件类型那个,文件访问权限位,文件长度和指向该文件所占用的数据库的指针等等。只有两项数据存放在目录项中:文件名和I节点编号。I节点编号的数据类型为ino_t。
3.每个文件系统各自对他们的I节点进行编号,因此目录中I节点编号数指向同一文件系中的相应I节点,不能使一个目录项指向另一个文件系统的I节点。这就是为什么ln(1)命令不能跨越文件系统的原因。
4.15 link unlink remove 和 rename函数
#include <unistd.h>
int link(const char * existingpath,const char * newpath); 返回值:若成功则返回0,若出错则返回-1
#include <unistd.h>
int unlink(const char * pathname); 返回值:若成功则返回0, 若出错则返回-1
此函数删除目录项,并将由pathname所应用文件的连接数减一。如果该目录设置了粘住位,则对该目录必须具有写权限,并且具备下面三个条件之一:1.拥有该文件 2.拥有该目录 3.具有超级用户特权。
只有当链接计数达到0时,该文件的内容才可被删除。另一个条件也会组织删除文件的内容---只要有进程打开了该文件,其内容也不能删除。关闭一个文件时,内核首先检查打开该文件的进程数,如果数达到0,然后内核检查其连接数,如果这个数也是0,那么就删除该文件的内容。
eg :打开一个文件,然后unlink它
#include "apue.h"
#include <fcntl.h>
int main(void)
{
if (open("tempfile", O_RDWR) < 0)
err_sys("open error");
if (unlink("tempfile") < 0)
err_sys("unlink error");
printf("file unlinked\n");
sleep(15);
printf("done\n");
exit(0);
}
如果 pathname是符号链接,那么unlink删除该符号链接,而不会删除该链接所引用的文件。给出符号链接名情况下,没有一个函数能删除该链接所引用的文件。
超级用户可以调用unlink,其参数pathname制定一个目录,当时通常应该使用rmdir函数,而不使用这种方式。
我们也可以用remove函数接触对一个文件或目录的链接。对于文件,remove的功能与unlink相同。对于目录remove的功能与rmdir相同。
#include <stdio.h>
int remove(const char * pathname); 返回值:成功返回0 出错返回-1;
#include < stdio.h>
int rename(const char * oldname, const char * newname); 返回值:成功返回0 ,出错返回-1
4.16 符号链接
4.17 symlink 和 readlink函数
#include <unistd.h>
int symlink(const char * actualpath, const char * sympath); //若成功则返回0 若出错则返回-1
因为open函数跟随符号链接,所以需要有一种方法打开该链接本身,并读该链接中的名字。readlink函数提供了这种功能
#include<unistd.h>
sssize_t readlink (const char * restrict pathname ,char * restrict buf , size_t bufsize);成功返回读到的字节数,出错返回-1
4.18 文件的时间
系统并不保存对一个I节点的最后一次访问时间,所以access和stat函数并不更改这三个时间中的任一个。
ls命令按这三个时间值中的一个排序进行显示。-u选项使其用访问的时间排序, -c 选项则使其用更改状态时间排序。
4.19 utime函数
#include<utime.h>
int utime(const char * pathname, const struct utimbuf *times); 若成功返回0 若出错返回-1
struct utimbuf {
time_t actime; /* access time */
time_t modtime; /* modification time */
}
如果times是一个空指针,则返回时间和修改时间两者都设置为当前时间。为了执行此操作必须满足下列两条件之一:进程的有效用户ID必须等于该文件的所有者ID;或者进程对该文件必须具有写权限。
如果times是非空指针,则访问时间和修改时间被设置为times所指向结构中的值。此时进程有效用户ID必须等于该文件的所有者ID,或者进程必须是一个超级用户进程。对文件只具有写权限是不够的。
我们不能对更改状态时间st_ctime制定一个值,当调用utime函数时,此字段将自动更新。
以下程序使用带O_TRUNC选项的open函数将文件长度截短卫0,但并不更改其访问时间及修改时间
#include "apue.h"
#include <fcntl.h>
#include <utime.h>
int main(int argc, char *argv[])
{
int i, fd;
struct stat statbuf;
struct utimbuf timebuf;
for (i = 1; i < argc; i++) {
if (stat(argv[i], &statbuf) < 0) { /* fetch current times */
err_ret("%s: stat error", argv[i]);
continue;
}
if ((fd = open(argv[i], O_RDWR | O_TRUNC)) < 0) { /* truncate */
err_ret("%s: open error", argv[i]);
continue;
}
close(fd);
timebuf.actime = statbuf.st_atime;
timebuf.modtime = statbuf.st_mtime;
if (utime(argv[i], &timebuf) < 0) { /* reset times */
err_ret("%s: utime error", argv[i]);
continue;
}
}
exit(0);
}
4.20 mkdir 和 rmdir函数
#include <sys/stat.h>
int mkdir(const char * pathname , mode_t mode); 返回值:成功返回0 出错则返回-1
#include <unistd.h>
int rmdir(const char * pathname); 返回值:成功则返回0 出错则返回-1;
4.21读目录
#include "apue.h" #ifdef SOLARIS #include <sys/mkdev.h> #endif int main(int argc, char *argv[]) { int i; struct stat buf; for (i = 1; i < argc; i++) { printf("%s: ", argv[i]); if (stat(argv[i], &buf) < 0) { err_ret("stat error"); continue; } printf("dev = %d/%d", major(buf.st_dev), minor(buf.st_dev)); if (S_ISCHR(buf.st_mode) || S_ISBLK(buf.st_mode)) { printf(" (%s) rdev = %d/%d", (S_ISCHR(buf.st_mode)) ? "character" : "block", major(buf.st_rdev), minor(buf.st_rdev)); } printf("\n"); } exit(0); }