#include <sys/stat.h>
int stat(const char *restrict pathname, struct stat *restrict buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *restrict pathname, struct stat *restrict buf);
int lstat(int fd, const char *restrict pathname, struct stat *restrict buf, int flag);
/* 所有4个函数的返回值:若成功,返回0;若出错,返回-1 */
sturct 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 links */
gid_t st_gid; /* group ID of owner */
off_t st_size; /* size in bytes, for regular files */
struct timespec st_atime; /* time of last access */
struct timespec st_mtime; /* time of last modification */
struct timespec 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 */
}
确定文件类型
宏 | 文件类型 |
---|---|
S_ISREG() | 普通文件 |
S_ISDIR() | 目录文件 |
S_ISCHR() | 字符特殊文件 |
S_ISBLK() | 块特殊文件 |
S_ISFIFO() | 管道或FIFO |
S_ISLNK() | 符号链接 |
S_ISSOCK() | 套接字 |
例子:取命令行参数然后针对每一个命令行打印其文件类型
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
int main(int argc, char** argv)
{
int i;
char* ptr;
struct stat buf;
for (i = 1; i < argc; ++i)
{
printf("%s: ", argv[i]);
if (lstat(argv[i], &buf) < 0)
{
printf("lstat error!\n");
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";
printf("%s\n", ptr);
}
exit(0);
}
yanke@yanke-pc:~/yanke/Code/apue/ch4$ ./a.out /etc/passwd /etc/ /dev/log \
> /dev/tty /var/lib/oprofile/opd_pipe /dev/sr0 /dev/cdrom
/etc/passwd: regular
/etc/: directory
/dev/log: socket
/dev/tty: character special
/var/lib/oprofile/opd_pipe: lstat error!
/dev/sr0: lstat error!
/dev/cdrom: lstat error!
各ID名字 | 含义 |
---|---|
实际用户ID和实际组ID | 我们实际是谁 |
有效用户ID,有效组ID和附属组ID | 用于文件访问权限检查 |
保存的设置用户ID和保存的设置组ID | 由exec函数保存 |
st_mode成员包含了文件的访问权限位。
用户rwx、组rwx和其他rwx,可以使用chmod命令改变。
st_mode屏蔽 | 含义 |
---|---|
S_IRUSR | 用户读 |
S_IWUSR | 用户写 |
S_IXUSR | 用户执行 |
S_IRGRP | 组读 |
S_IWGRP | 组写 |
S_IXGRP | 组执行 |
S_IROTH | 其他读 |
S_IWOTH | 其他写 |
S_IXOTH | 其他执行 |
文件访问如下规则:
* 打开任意类型文件,需要对保护该名字的每一个目录都有执行权限
* 对于一个文件的读权限决定了我们能不能打开文件进行读操作。
* 对于一个文件的写权限决定了我们能不能打开文件进行写操作。
* 为了在一个目录中创建新文件,必须对包含该文件的目录具有写权限和执行权限
新文件的用户ID设置为进程的有效用户ID
关于组ID:可以是进程的有效组ID,也可以是它所在目录的组ID
函数access和faccessat是按照实际用户ID和实际组ID来进行访问权限设置的。faccessat函数更加强大,既可以测试实际用户ID,也可以测试有效用户ID,除非是少数情况,大部分都是使用faccessat函数.
#include <unistd.h>
int access(const char *path, int amode);
int faccessat(int fd, const char *path, int mode, int flag);
access函数的用法:
// use access function
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char** argv)
{
if (argc != 2)
{
printf("usage : a.out <pathname>\n");
exit(1);
}
if (access(argv[1],R_OK) < 0)
{
printf("access error for %s\n", argv[1]);
}
else
{
printf("read access OK\n");
}
if (open(argv[1], O_RDONLY) < 0)
{
printf("open error for %s\n", argv[1]);
}
else
{
printf("open for reading OK\n");
}
exit(0);
}
为进程设置屏蔽字,返回之前的值。
#include <sys/stat.h> mode_t umask(mode_t cmask);
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#define RWRWRW (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
int main()
{
umask(0);
if (creat("foo", RWRWRW) < 0)
{
printf("creat foo error!\n");
exit(1);
}
umask(S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
if (creat("bar", RWRWRW) < 0)
{
printf("creat bar error!\n");
exit(1);
}
exit(0);
}
yanke@vm:~/Code/apue/ch4$ umask
0002
yanke@vm:~/Code/apue/ch4$ ll bar foo
-rw------- 1 yanke yanke 0 8月 31 18:39 bar
-rw-rw-rw- 1 yanke yanke 0 8月 31 18:39 foo
shell启动时,它会读取启动文件的设置从而在启动时自行设置为默认的掩码字,在运行过程中,用户可以使用umask命令手动更改掩码字。shell打开一个进程,新的进程则会继承shell的umask值,在使用Unix的时候,经常需要使用的mkdir、touch之类的创建文件的命令,这些命令在运行的时候就是继承shell的umask。
因此:如果想要保证自己能完全指定文件的权限,那么必须在运行时使用umask函数修改为0,否则非0得umask值可能会关闭我们需要的权限位置,当然,如果进程不需要关心文件的权限问题,那么完全可以指定rwxrwxrwx或者rwrwrw的权限,然后umask会自动根据默认值将其修改。
#include <sys/stat.h>
int chmod(const char *path, mode_t mode);
int fchmod(int fildes, mode_t mode);
int chmodat(int fd, const char *path, mode_t mode, int flag);
全部的权限位如下:
mode | 说明 |
---|---|
S_ISUID | 执行时设置用户ID |
S_ISGID | 执行时设置组ID |
S_ISVTX | 保存正文(粘着位) |
S_IRWXU | 用户(所有者)读、写和执行 |
S_IRUSR | 用户(所有者)读 |
S_IWUSR | 用户(所有者)写 |
S_IXUSR | 用户(所有者)执行 |
S_IRWXG | 组读、写和执行 |
S_IRGRP | 组读 |
S_IWGRP | 组写 |
S_IXGRP | 组执行 |
S_IRWXO | 其他读、写和执行 |
S_IROTH | 其他读 |
S_IWOTH | 其他写 |
S_IXOTH | 其他执行 |
#define S_ISUID __S_ISUID /* Set user ID on execution. */
#define S_ISGID __S_ISGID /* Set group ID on execution. */
#define S_ISVTX __S_ISVTX
#define S_IRUSR __S_IREAD /* Read by owner. */
#define S_IWUSR __S_IWRITE /* Write by owner. */
#define S_IXUSR __S_IEXEC /* Execute by owner. */
#define S_IRWXU (__S_IREAD|__S_IWRITE|__S_IEXEC)
#define S_IRGRP (S_IRUSR >> 3) /* Read by group. */
#define S_IWGRP (S_IWUSR >> 3) /* Write by group. */
#define S_IXGRP (S_IXUSR >> 3) /* Execute by group. */
#define S_IRWXG (S_IRWXU >> 3)
#define S_IROTH (S_IRGRP >> 3) /* Read by others. */
#define S_IWOTH (S_IWGRP >> 3) /* Write by others. */
#define S_IXOTH (S_IXGRP >> 3) /* Execute by others. */
#define S_IRWXO (S_IRWXG >> 3)
#define __S_ISUID 04000 /* Set user ID on execution. */
#define __S_ISGID 02000 /* Set group ID on execution. */
#define __S_ISVTX 01000 /* Save swapped text after use (sticky). */
#define __S_IREAD 0400 /* Read by owner. */
#define __S_IWRITE 0200 /* Write by owner. */
#define __S_IEXEC 0100 /* Execute by owner. */
//test chmod
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int main()
{
struct stat statbuf;
//
if (stat("foo", &statbuf) < 0)
{
printf("stat error for foo\n");
exit(1);
}
if (chmod("foo", (statbuf.st_mode & ~S_IXGRP) | S_ISGID) < 0)
{
printf("chmod error for foo\n");
exit(1);
}
if (chmod("bar", S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0)
{
printf("chmod error for bar\n");
exit(-1);
}
exit(0);
}
yanke@vm:~/Code/apue/ch4$ ll foo bar
-rw-r--r-- 1 yanke yanke 0 8月 31 18:39 bar
-rw-rwSrw- 1 yanke yanke 0 8月 31 18:39 foo
粘着位,又称保存文本位,其实就是一个权限控制属性。通常情况下,粘着位即可以用在普通文件上,也可以用在目录文件上。当用在普通文件上时,粘着位可以把某个程序文件的SUID置位。并且它的文本映像将永久保存在交换区里。如此的话,当Unix操作系统程序获得了CPU使用权时,就可以快速的装载到内存中。故粘着位可以提高系统程序的运行效率。
如有些版本的Unix系统,就把vi等常用的程序文件的粘着位设置为1,就明显提高了这些应用程序的运行效率。不过现在很少用到这个特性。因为现在磁盘读写速度已经达到极致;而且内存的价格也便宜。
也就是说,现在磁盘速度与内存往往已经不是系统的瓶颈资源,故为普通文件设置粘着位已经没有特殊的必要了。为此笔者现在在系统设置中,基本上不会为普通文件设置粘着位。
chown、fchown、fchownat和lchown用于更用户ID和组ID。如果两个参数owner或group中的任意一个是-1,则对应的ID不变。
#include <unistd.h>
int chown(const char *pathname, uid_t owner, git_t group);
int fchown(int fd, uid_t owner, gid_t group);
int fchownat(int fd, const char *pathname, uid_t owner, gid_t group, int flag);
int lchown(const char *pathname, uid_t owner, gid_t group);
//返回值:若辰宫,返回0;若出错,返回-1
stat结构成员st_size表示以字节为单位的单位长度。只对普通文件、目录文件和符号连接有效。
空洞是由所设置的偏移量超过文件端尾,并写入了某些数据后造成的。
如果该文件以前的长度大于length,则超过length以外的数据就不能再访问。如果以前的长度小于length,文件长度将增加,在以前的文件尾端和新的文件尾端之间的数据将读作0。
include <unistd.h>
int truncate(const char *pathname, off_t length);
int ftruncate(int fd, off_t length);
//返回值:若成功,返回0;若失败,返回-1
创建一个指向现有文件的链接
#include <unistd.h>
int link(const char *existingpath, const char *newpath);
int linkat(int efd, const char *existingpath, int nfd, const char *newpath, int flag);
// 两个函数的返回值:若成功,返回0;若出错,返回-1
删除一个现有目录项
#include <unistd.h>
int unlink(const char *pathname);
int unlinkat(int fd, const char *pathname, int flag);
// 两个函数的返回值:若成功,返回0;若出错,返回-1
使用remove删除
#include <stdio.h>
int remove(const char *pathname);
// 返回值:若成功,返回0;若失败,返回-1
#include <stdio.h>
int rename(const char *oldname, const char *newname);
int renameat(int oldfd, const char *oldname, int newfd, const char *newname);
符号链接,即通常说的软链接,是对一个文件的间接指针。硬链接是直接指向索引块,而软链接实际上是一个文件。
硬链接的限制:
- 硬链接通常要求链接和文件位于同一文件系统中。
- 只有超级用户才能创建指向目录的硬链接。
对符号链接以及他指向何种对象并无任何文件系统限制。任何用户都可以创建指向目录的符号链接。符号链接一般用于将一个文件或这个目录结构移动到系统中的另一个位置。
创建目录,其中.和..目录项是自动创建的。目录项通常至少要设置一个执行权限位,以允许访问该目录中的文件名。
#include <unistd.h>
int symlink(const char *actualpath, const char *sympath);
int symlinat(const char *acutalpath, int fd, const char *symptah);
// 两个函数的返回值:若成功,返回0;若出错,返回-1
读取符号链接
#include <unistd.h>
ssize_t readlink(const char *actualpath, const char *symptah);
sszie_t readlinkat(int fd, const char* restrict pathname, char *restrict buf, size_t bufsize);
// 两个函数的返回值:若成功,返回读取的字节数;若出错,返回-1
字段 | 说明 | 例子 | ls(1)选项 |
---|---|---|---|
st_atim | 文件数据的最后访问时间 | read | -u |
st_mtim | 文件数据的最后修改时间 | write | 默认 |
st_ctim | i节点状态的最后更改时间 | chomd、chown | -c |
修改文件访问时间
#include <sys/stat.h>
int futimens(int fd, const struct timespec times[2]);
int utimensat(int fd, const char *path, const struct timespec times[2], int flag);
//两个函数返回值:若成功,返回0;若出错,返回-1
使用futimens
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <time.h>
int main(int argc, char** argv)
{
int i, fd;
struct stat statbuf;
struct timespec times[2];
for (i = 1; i < argc; ++i)
{
//get current times
if (stat(argv[0], &statbuf) < 0)
{
printf("%s:stat error\n", argv[i]);
continue;
}
if ((fd=open(argv[i], O_RDWR|O_TRUNC)) < 0)
{
printf("%s:open error\n", argv[i]);
continue;
}
times[0] = statbuf.st_atim;
times[1] = statbuf.st_mtim;
if (futimens(fd, times) < 0)
{
printf("%s:futimes error\n", argv[i]);
}
close(fd);
}
exit(0);
}
yanke@vm:~/Code/apue/ch4$ touch changemod times
yanke@vm:~/Code/apue/ch4$ ll changemod times
-rw-rw-r-- 1 yanke yanke 0 8月 31 21:55 changemod
-rw-rw-r-- 1 yanke yanke 0 8月 31 21:55 times
yanke@vm:~/Code/apue/ch4$ ls -lu changemod times
-rw-rw-r-- 1 yanke yanke 0 8月 31 21:55 changemod
-rw-rw-r-- 1 yanke yanke 0 8月 31 21:55 times
yanke@vm:~/Code/apue/ch4$ ls -lc changemod times
-rw-rw-r-- 1 yanke yanke 0 8月 31 21:55 changemod
-rw-rw-r-- 1 yanke yanke 0 8月 31 21:55 times
yanke@vm:~/Code/apue/ch4$ ./a.out changemod times
yanke@vm:~/Code/apue/ch4$ ls -lu changemod times
-rw-rw-r-- 1 yanke yanke 0 8月 31 21:55 changemod
-rw-rw-r-- 1 yanke yanke 0 8月 31 21:55 times
yanke@vm:~/Code/apue/ch4$ ls -lc changemod times
-rw-rw-r-- 1 yanke yanke 0 8月 31 21:57 changemod
-rw-rw-r-- 1 yanke yanke 0 8月 31 21:57 times
include <sys/stat.h>
int mkdir(const char *pathname, mode_t mode);
int mkdir(int fd, const char *pathname, mode_t mode);
//两个函数返回值:若成功,返回0;若出错,返回-1
#include <unistd.h>
int rmdir(const char *pathname);
//返回值:若成功,返回0;若失败,返回-1
#include <dirent.h>
DIR *opendir(const char *pathname);
DIR *fdopendir(int fd);
/* 两个函数返回值:若成功,返回指针;若出错,返回NULL */
struct dirent *readdir(DIR *dp);
/* 返回值:若成功,返回指针;若在目录尾或出错,返回NULL */
void rewinddir(DIR *dp);
int closeddir(DIR *dp);
/* 返回值:若成功,返回0;若失败,返回-1 */
long telldir(DIR *dp);
/* 返回值:与dp关联的相关位置 */
void seekdir(DIR *dp, long loc);
DIR结构体的定义如下。DIR结构体类似于FILE,是一个内部结构。
struct __dirstream
{
void *__fd;
char *__data;
int __entry_data;
char *__ptr;
int __entry_ptr;
size_t __allocation;
size_t __size;
__libc_lock_define (, __lock)
};
typedef struct __dirstream DIR;
struct dirent
{
long d_ino; /* inode number 索引节点号 */
off_t d_off; /* offset to this dirent 在目录文件中的偏移 */
unsigned short d_reclen; /* length of this d_name 文件名长 */
unsigned char d_type; /* the type of d_name 文件类型 */
char d_name [NAME_MAX+1]; /* file name (null-terminated) 文件名,最长255字符 */
}
通过readdir函数读取到的文件名存储在结构体dirent的d_name成员中,而函数
int stat(const char *file_name, struct stat *buf);
的作用就是获取文件名为d_name的文件的详细信息,存储在stat结构体中。以下为stat结构体的定义:
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_gid; //组识别号
off_t st_size; //以字节为单位的文件容量
time_t st_atime; //最后一次访问该文件的时间
time_t st_mtime; //最后一次修改该文件的时间
time_t st_ctime; //最后一次改变该文件状态的时间
blksize_t st_blksize; //包含该文件的磁盘块的大小
blkcnt_t st_blocks; //该文件所占的磁盘块
};
想要获取某目录下(比如a目下)b文件的详细信息,我们应该怎样做?
- 使用opendir函数打开目录a,返回指向目录a的DIR结构体c。
- 调用readdir( c)函数读取目录a下所有文件(包括目录),返回指向目录a下所有文件的dirent结构体d。
- 遍历d,调用stat(d->name,stat *e)来获取每个文件的详细信息,存储在stat结构体e中。
总体就是这样一种逐步细化的过程,在这一过程中,三种结构体扮演着不同的角色。
#include <unistd.h>
int chdir(const char *pathname);
int fchdir(int fd);
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
if (chdir("/tmp") < 0)
{
printf("chdir failed\n");
exit(1);
}
printf("chdir to /tmp succeeded\n");
exit(0);
}
yanke@vm:~/Code/apue/ch4$ pwd
/home/yanke/Code/apue/ch4
yanke@vm:~/Code/apue/ch4$ ./a.out
chdir to /tmp succeeded
yanke@vm:~/Code/apue/ch4$ pwd
/home/yanke/Code/apue/ch4
char *getcwd(char *buf, size_t size);
[1] http://blog.csdn.net/u012927281/article/details/51737312
[2] https://segmentfault.com/a/1190000004416055#articleHeader8
[3] http://www.liweifan.com/2012/05/13/linux-system-function-files-operation/