如果我们希望知道文件的各种信息和特征,stat结构可以包含文件的所有属性。
我们可以通过下面三个函数来得到文件的所有属性和信息:
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。
其中buf参数结构式我们必须提供来存储文件信息的。
structstat.c:
struct stat
{
mode_t st_mode;
ino_t st_ino;
dev_t st_dev;
dev_t st_rdev;
nlink_t st_nlink;
uid_t st_uid;
gid_t st_git;
off_t st_size;
time_t st_atime;
time_t st_mtime;
time_t st_ctime;
}
4.3文件类型
普通文件,目录文件,快特殊文件,字符特殊文件,FIFO,套接字(socket),符号链接。文件类型信息包含在stat结构的st_mode成员中。可以用下面的宏来确认文件类型,st_mode是他们的参数。
<sys/stat.h>中的文件类型宏
S_ISREG() 普通文件
S_ISDIR() 目录文件
S_ISCHR() 字符特殊文件
S_ISBLK() 块特殊文件
S_ISFIFO() 管道或者FIFO
S_ISLNK() 符号链接
S_ISSOCK() 套接字
下面的程序对每个命令行参数打印文件类型:
stattest.c:
#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);
}
运行结果如下:
[root@localhost apue]# gcc -o stattest.out stattest.c
[root@localhost apue]# ./ stattest.out /etc/passwd /etc /dev/initctl /dev/log\
> /dev/tty /dev/scsi/host0/bus0/target0/lun0/cd /dev/cdrom
bash: ./: is a directory
[root@localhost apue]# ./stattest.out /etc/passwd /etc /dev/initctl /dev/log/dev/tty /dev/scsi/host0/bus0/target0/lun0/cd /dev/cdrom
/etc/passwd: regular
/etc: directory
/dev/initctl: FIFO
/dev/log/dev/tty: lstat error: Not a directory
/dev/scsi/host0/bus0/target0/lun0/cd: lstat error: No such file or directory
/dev/cdrom: symbolic link
[root@localhost apue]# ./stattest.out /etc/passwd /etc /dev/initctl /dev/log /dev/tty /dev/scsi/host0/bus0/target0/lun0/cd /dev/cdrom
/etc/passwd: regular
/etc: directory
/dev/initctl: FIFO
/dev/log: socket
/dev/tty: character special
/dev/scsi/host0/bus0/target0/lun0/cd: lstat error: No such file or directory
/dev/cdrom: symbolic link
4.4设置用户ID和设置组ID
与进程相关的ID共有六个或者更多。实际用户ID,实际组ID,有效用户ID,有效组ID,附加组ID,保存的设置用户ID,保存的设置组ID。通常有效用户ID等于实际用户ID,有效组ID等于实际组ID。分别由stat结构中的st_uid和st_gid来确定。
4.5文件访问权限
stat结构中的st_mode值也包含针对文件的访问权限位。所有的文件类型都有访问权限。
有效用户ID就是执行这个进程的用户的ID,实际用户ID就是文件的所有者。组是同样的结果。
当我们用open或者creat,mkdir新建一个目录或者文件的时候,文件的所有者ID就是进程的有效用户ID,
组ID要么是有效组ID或者它所在目录的ID。
4.7 access函数
我们有时需要测试实际用户和组对文件的访问权限,access函数是按实际用户ID和组ID进行访问权限测试的。
int access(const char *pathname, int mode);
返回:成功返回0,错误返回-1。
mode参数是按下面的几个值按位或。
mode 说明
R_OK 测试读权限
R_OK 测试写权限
X_OK 测试执行权限
F_OK 测试文件是否存在
access函数实例:
access.c:
#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);
}
执行结果:
[root@localhost apue]# gcc -o access.out access.c
[root@localhost apue]# ls -l access.out
-rwxr-xr-x 1 root root 7025 12-28 17:51 access.out
[root@localhost apue]# ./access.out access.out
read access ok
open for reading ok
[root@localhost apue]# ./access.out /etc/shadow
read access ok
open for reading ok
[root@localhost apue]# su - huangcd
[huangcd@localhost ~]$ cd /apue
[huangcd@localhost apue]$ ./access.out /etc/shadow
access error for /etc/shadow: Permission denied
open error for /etc/shadow: Permission denied
4.8umask函数
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 |
#include "apue.h"
#include <fcntl.h>
#define RWRWRW (S_IRUSR|S_IWUSR|S_IWGRP|S_IRGRP|S_IROTH|S_IWOTH)
int main(void)
{
umask(0);
if (creat("foo", RWRWRW) < 0)
err_sys("creat error for foo");
umask(S_IWGRP|S_IRGRP|S_IROTH|S_IWOTH);
if (creat("bar", RWRWRW) < 0)
err_sys("creat error for bar");
exit(0);
}
我们可以从结果发现,umask设置的屏蔽位已经覆盖了creat函数设置的屏蔽位。
运行结果:
[huangcd@localhost apue]$ gcc -o umasktest.out umasktest.c
[huangcd@localhost apue]$ umask
0002
[huangcd@localhost apue]$ ./umasktest.out
[huangcd@localhost apue]$ ls -l foo bar
-rw------- 1 huangcd huangcd 0 12-28 18:20 bar
-rw-rw-rw- 1 huangcd huangcd 0 12-28 18:20 foo
4.9chmod和fchmod函数
这两个函数可用用于改变现有文件的访问权限。
int chmod(const char *pathname, mode_t mode);
int fchmod(int filedes, mode_t mode);
mode参数在下面中的一个或者多个或,在<sys/stat.h>中定义。
mode | Description |
---|---|
S_ISUID | set-user-ID on execution |
S_ISGID | set-group-ID on execution |
S_ISVTX | saved-text (sticky bit) |
S_IRWXU | read, write, and execute by user (owner) |
| read by user (owner) |
| write by user (owner) |
| execute by user (owner) |
S_IRWXG | read, write, and execute by group |
| read by group |
| write by group |
| execute by group |
S_IRWXO | read, write, and execute by other (world) |
| read by other (world) |
| write by other (world) |
| execute by other (world) |
chmod函数实例:
chmodtest.c:
#include "apue.h"
int main(void)
{
struct stat statbuf;
if (stat("foo", &statbuf) < 0)
err_sys("stat error for foo");
/*turn on set-group-id and trun off group-execute*/
if (chmod("foo", (statbuf.st_mode & ~S_IXGRP) | S_ISGID) < 0)
err_sys("chmod error for foo");
/*set absolute mode to rw-r--r--*/
if (chmod("bar", S_IRUSR| S_IWUSR| S_IRGRP| S_IROTH) < 0)
err_sys("chmod error for bar");
exit(0);
}
运行结果:
[huangcd@localhost apue]$ vim chmodtest.c
[huangcd@localhost apue]$ ls -l bar foo
-rw------- 1 huangcd huangcd 0 12-28 18:20 bar
-rw-rw-rw- 1 huangcd huangcd 0 12-28 18:20 foo
[huangcd@localhost apue]$ gcc -o chmodtest.out chmodtest.c
[huangcd@localhost apue]$ ./chmodtest.out
[huangcd@localhost apue]$ ls -l bar foo
-rw-r--r-- 1 huangcd huangcd 0 12-28 18:20 bar
-rw-rwSrw- 1 huangcd huangcd 0 12-28 18:20 foo
如果一个文件设置了粘住位,那么在文件第一次执行以后,保留程序正文部分的一个副本在交换区。可以通过S_ISVTX来设置粘住位。
4.11chown、fchown和lchown函数
下面几个函数可以用于更改文件的用户ID和组ID。
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);
如果参数owner和group中的任何一个值是-1,那么对应的ID不变。
4.12文件长度
stat结构成员st_size表示以字节为单位的文件长度,此字段只针对普通文件,目录文件和连接文件有效。
文件空洞是由于所设置的偏移量超过文件尾端,并写入某些数据造成的。对于没有写入的空洞,read读入的字节是0。
复制有空洞的文件的时候,所有的空洞都被填满。
4.13文件截短
我们可以通过在打开文件时设置O_TRUNC标志来将一个文件清空为0。
下面的函数可以用于文件的截短:
int truncate(const char *pathname, off_t length);
int ftruncate(int filedes, off_t length);
这两个函数把文件的长度截短为length长。
4.19utime函数
一个文件的访问和修改时间可以用utime函数来实现。
int utime(const char *pahtname, const struct utimebuf *times)
参数中的time的结构如下:
struct utimebuf{
time_t actime;
time_t modtime;
}
utime函数实例:
#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)
{
err_ret("%s: stat errir", argv[i]);
continue;
}
if ((fd = open(argv[i], O_RDWR| O_TRUNC)) < 0)
{
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)
{
err_ret("%s: utime error", argv[1]);
continue;
}
}
exit(0);
}
运行结果:
[root@localhost apue]# ls -l printId.out
-rwxr-xr-x 1 root root 5241 12-25 15:42 printId.out
[root@localhost apue]# ls -ul printId.out
-rwxr-xr-x 1 root root 5241 12-30 14:00 printId.out
[root@localhost apue]# date
2013年 12月 30日 星期一 14:20:28 CST
[root@localhost apue]# ./utimetest.out printId.out
[root@localhost apue]# ls -l printId.out
-rwxr-xr-x 1 root root 0 12-25 15:42 printId.out
[root@localhost apue]# ls -ul printId.out
-rwxr-xr-x 1 root root 0 12-30 14:20 printId.out
[root@localhost apue]# ls -cl printId.out
-rwxr-xr-x 1 root root 0 12-30 14:20 printId.out
4.20mkdir和rmdir函数
新建一个目录,pahtname是一个空目录。mode是新建目录的权限值。
int mkdir(const char *pathname, mode_t mode)
若成功返回0,出错返回-1。
用rmdir和删除一个目录。空目录只包含.和..这两项的目录。
int rmdir(const char *pathname);
4.21读目录
我们可以通过下列函数来实现对一个目录的读取等操作:DIR * opendir(const char *pahtname) //成功返回指针,错误返回NULL
struct dirent * readdir(DIR *dp); //成功返回指针,错误返回目录结尾或者NULL
void rewinddir(DIR *dp) //成功返回0,错误返回-1
int closedir( DIR *dp) //成功返回0,错误返回-1.
long telldir( DIR *dp) //返回dp关联的目录中的当前位置。
void seekdir(DIR *dp, long loc) //返回dp关联的目录中的当前位置。
这些函数都是在头文件<dirent.h>中定义的啊。结构体的定义如下:
struct dirent{
ino_t d_no;
char d_name[NAME_MAX + 1]
}
DIR是一个内部结构,它用来保存当前正在被读取的目录的有关信息。
由opendir函数返回DIR结构的指针由其他五个函数操作。
4.22 chdir、fchdir和getcwp函数
每个进程都有一个当前工作目录,次目录是搜索所有相对路径名的起点。当前工作目录是进程的一个属性。
进程通过调用chdir和fchdir函数可以更改当前工作目录。
int chdir(const char *pahtname)
int fchdir(int filedes)
若成功返回0,若错误则返回-1。参数是指定的新的工作目录。
chdir函数实例:
chdirtest.c:
#include "apue.h"
int main(void)
{
if (chdir("/tmp") < 0)
err_sys("chdir failed");
printf("chdir to /tmp succeeded\n");
exit(0);
}
执行结果:
[root@localhost apue]# gcc -o chdirtest.out chdirtest.c
[root@localhost apue]# pwd
/apue
[root@localhost apue]# ./chdirtest.out
chdir to /tmp succeeded
[root@localhost apue]# pwd
/apue
我们虽然改变了当前进程的工作目录,但是并没有改变shell的工作目录,当前进程是shell进程的一个子进程。
内核中并没有保存工作目录的详细信息,只是保存了指向该目录的v节点的指针等目录本身的信息。并不保存该目录的完整路径。
我们可以通过getcwd函数来得到当前工作目录的完整路径。
char *getcwd(char *buf, size_t size)
参数一是缓冲地址的,第二个参数缓冲绝对路径长度的。
getcwd()函数实例:
getcwdtest.c:
#include "apue.h"
char* path_alloc(int* size);
int main(void)
{
char *ptr;
int size;
if (chdir("/home/huangcd") < 0)
err_sys("chdir failed");
ptr = path_alloc(&size);
if (getcwd(ptr, size) == NULL)
err_sys("getcwd failed");
printf("cwd = %s\n", ptr);
exit(0);
}
char * path_alloc(int *size)
{
char *p = NULL;
if(!size)
return NULL;
p = malloc(256);
if(p)
*size = 256;
else
*size = 0;
return p;
}
运行结果:
root@localhost apue]# gcc -o getcwdtest.out getcwdtest.c
[root@localhost apue]# ./getcwdtest.out
cwd = /home/huangcd
4.23特殊设备文件
在计算机系统中,每个设备都有主设备号和此设备号。设备号用的数据类型是dev_t类型。主设备号标识设备驱动程序,次设备号标识特定的子设备。
我们可以通过major和minor这两个宏来访问主、次设备号。
系统中的每个文件的stat成员st_dev的值是文件系统的设备号。只有字符特殊文件盒块特殊文件才有st_rdev值。次值包含设计设备的设备号。
下面实例打印st_dev和st_rdev值:
devtest.c:
#include "apue.h"
#ifdef SOLARIS
#include <sys/mkdev.h>
#endif
#include <sys/sysmacros.h>
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);
}
运行结果:
[root@localhost apue]# gcc -o devtest.out devtest.c
[root@localhost apue]# ./devtest.out / /home/huangcd /dev/tty[01]
/: dev = 8/2
/home/huangcd: dev = 8/3
/dev/tty0: dev = 0/17(character) rdev = 4/0
/dev/tty1: dev = 0/17(character) rdev = 4/1
4.24文件访问权限位小结
Constant | Description | Effect on regular file | Effect on directory |
---|---|---|---|
S_ISUID | set-user-ID | set effective user ID on execution | (not used) |
S_ISGID | set-group-ID | if group-execute set then set effective group ID on execution; otherwise enable mandatory record locking (if supported) | set group ID of new files created in directory to group ID of directory |
S_ISVTX | sticky bit | control caching of file contents (if supported) | restrict removal and renaming of files in directory |
S_IRUSR | user-read | user permission to read file | user permission to read directory entries |
S_IWUSR | user-write | user permission to write file | user permission to remove and create files in directory |
S_IXUSR | user-execute | user permission to execute file | user permission to search for given pathname in directory |
S_IRGRP | group-read | group permission to read file | group permission to read directory entries |
S_IWGRP | group-write | group permission to write file | group permission to remove and create files in directory |
S_IXGRP | group-execute | group permission to execute file | group permission to search for given pathname in directory |
S_IROTH | other-read | other permission to read file | other permission to read directory entries |
S_IWOTH | other-write | other permission to write file | other permission to remove and create files in directory |
S_IXOTH | other-execute | other permission to execute file | other permission to search for given pathname in directory |
最后九个常量分为三组:
S_IRWXU = S_IRUSR | S_IWUSR | S_IXUSR S_IRWXG = S_IRGRP | S_IWGRP | S_IXGRP
S_IRWXO = S_IROTH | S_IWOTH | S_IXOTH