APUE第四章文件和目录

与进程相关联的ID

1.实际用户ID+实际组ID
2.有效用户ID+有效组ID+附属组ID
3.保存的设置用户ID+保存的设置组ID

  • 执行一个程序文件时,进程的有效用户ID=实际用户ID,有效组ID=实际组ID,

    当我执行./a.out文件时,如果a.out此文件权限是root root,但是我以root用户来执行的(实际用户是非root用户,此时进程的有效用户id变成实际用户id,那么该进程的权限只有非root权限.

  • 但是可以修改文件的有效用户ID为该文件所有者的用户id(组同理),也就以非root用户身份执行文件时,文件拥有root权限 (文件本身属于root,通过修改st_mode中的特殊标志,设置用户ID位和设置组ID位)
  • 实例:

    程序passwd允许任一用户改变口令,因为改程序能将新口令写入口令文件,而只有root才有对该文件的写权限,

函数

  1. access,按实际用户ID和实际组ID进行访问权限测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <bits/stdc++.h>
#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
std::cout << "read access OK\n";
int fd;
// 只读的形式打开
if ((fd = open(argv[1],O_RDONLY)) < 0)
err_ret("open error for %s",argv[1]);
else
std::cout << "open for reading OK\n";
close(fd);

if (access(argv[1],W_OK) < 0)
err_ret("access error for %s",argv[1]);
else
std::cout << "write access Ok\n";
return 0;
}
  1. umask:为进程设置文件模式创建掩码,意思就是创建文件的权限位是在umask码的前提下才有
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <bits/stdc++.h>
#include "apue.h"
#include <fcntl.h>

#define RWRWRW (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
#define __RWRW (S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)


int main(int argc,char *argv[]) {
// 首先设置 文件模式创建掩码
// 111 111 111 所有权限为打开
umask(0);
// 111 111 111
// 110 110 110
// -----------
// 110 110 110
if (creat("foo",RWRWRW) < 0)
err_sys("creat error for foo");
// 相当于关闭了 g rw o rw
// 111 001 001
// 110 110 110
// -----------
// 110 000 000
umask(__RWRW);
if (creat("bar",RWRWRW) < 0)
err_sys("creat error for bar");
return 0;
}
  • 相当于清空原本的umask值,重新设定,然后提权限

3.link创建软连接和硬链接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <bits/stdc++.h>
#include "apue.h"
#include <unistd.h>
#include <fcntl.h>

int main(int argc,char *argv[]) {
std::string path = "/home/luchao/Documents/Apue/4/1.in";
std::string newpath = "/home/luchao/Documents/Apue/4/2.in";

//int status = link(path.c_str(),newpath.c_str());
int status = symlink(path.c_str(),newpath.c_str());
if (status == -1)
err_sys("link errro");
return 0;
}

递归降序遍历目录层次结构,按文件类型计数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#include "apue.h"
#include <dirent.h>
#include <errno.h>
#include <limits.h>
#include "path_alloc.h"

// 给函数声明类型
typedef int Myfunc(const char *,const struct stat *,int );

static Myfunc myfunc;
static int myftw(char *,Myfunc *);
static int dopath(Myfunc *);
static long nreg,ndir,nblk,nchr,nfifo,nslink,nsock,ntot;



int main(int argc,char *argv[]) {
if (argc != 2)
err_quit("usage: ftw <starting-pathname>");
// 整个文件主要是,首先 myftw这个函数
// 没错ret就是0,否则ret会返回具体的错误code
int ret = myftw(argv[1],myfunc);
ntot = (nreg + ndir + nblk + nchr + nfifo + nslink + nsock);
if (ntot == 0)
ntot = 1;

// 打印文件信息
printf("regular files= %10ld,%5.2f %%\n",nreg,nreg*100.0/ntot);
printf("directories= %10ld,%5.2f %%\n",ndir,ndir*100.0/ntot);
printf("block special= %10ld,%5.2f %%\n",nblk,nblk*100.0/ntot);
printf("char special= %10ld,%5.2f %%\n",nchr,nchr*100.0/ntot);
printf("FIFOS = %10ld,%5.2f %%\n",nfifo,nfifo*100.0/ntot);
printf("symbolic links= %10ld,%5.2f %%\n",nslink,nslink*100.0/ntot);
printf("sockets = %10ld,%5.2f %%\n",nsock,nsock*100.0/ntot);

return (ret);
}

#define FTW_F 1
#define FTW_D 2
#define FTW_DNR 3
#define FTW_NS 4

static char *fullpath;
static size_t pathlen;

// 形参是函数指针,传递Myfunc函数
static int myftw(char *pathname,Myfunc *func) {
// 分配PATH_MAX + 1 个字节的内存,然后保存在 pathlen中
fullpath = (char *)path_alloc(&pathlen);

// 默认分配长度小于 strlen pathname
if (pathlen <= strlen(pathname)) {
// 新长度
pathlen = strlen(pathname) * 2;
// 如果重新分配失败
if ((fullpath = (char *)realloc(fullpath,pathlen)) == NULL)
err_sys("realloc failed");
}

// 将地址pathname复制到fullpath
strcpy(fullpath,pathname);
return (dopath(func));
}
// 上面函数主要作用就是分配内存来存放fullpath
// 调用 dopath();函数

static int dopath(Myfunc *func) {
// 文件信息
struct stat statbuf;
// 目录的具体信息
struct dirent *dirp;
// 打开目录后返回值
DIR *dp;
int ret,n;
// lstat 避免得到符号链接指向的文件信息
if (lstat(fullpath,&statbuf) < 0)
// type = FTW_NS 读取文件状态失败,得到错误返回值
return (func(fullpath,&statbuf,FTW_NS));
// fullpath 目录项不是目录
if (S_ISDIR(statbuf.st_mode) == 0)
// 统计此文件信息.因为不是目录
return (func(fullpath,&statbuf,FTW_F));
// 当做目录交给func检查,结果ret != 0 出错
if ((ret = func(fullpath,&statbuf,FTW_D)) != 0)
// error code ret
return (ret);

n = strlen(fullpath);
// 扩展路径缓冲
if (n + NAME_MAX + 2 > pathlen) {
pathlen *= 2;
// 重新分配内存,判断是否成功,扩大内存,原区域内存内容不变
if ((fullpath = (char *)realloc(fullpath,pathlen)) == NULL)
err_sys("realloc failed");
}
// if /home n=5 /home/0 n=6
fullpath[n++] = '/';
fullpath[n] = 0;
// 打开目录,
if ((dp = opendir(fullpath)) == NULL)
// func检测目录不能读的error code
return (func(fullpath,&statbuf,FTW_DNR));
// 读取DIR中的每个目录项
while((dirp = readdir(dp)) != NULL) {
// 当前目录和父目录不显示
if (strcmp(dirp->d_name,".") == 0 || strcmp(dirp->d_name,"..") == 0)
continue;
// 复制到原来的path结尾处,/home/adcb n=6,子目录项也是目录
strcpy(&fullpath[n],dirp->d_name);
// 递归调用,
if ((ret = dopath(func)) != 0)
break;
}
// 消除 '/'
fullpath[n-1] = 0;
// DIR作为参数
if (closedir(dp) < 0)
err_ret("can't close directory %s",fullpath);
// 返回error code
return (ret);
}

// dopath 函数 return 时调用 Myfunc函数

static int
myfunc(const char *pathname,const struct stat *statptr,int type) {
// 判断传入的type是什么文件类型
switch(type) {
// 普通文件
case FTW_F:
// 求出普通文件的类型
switch(statptr->st_mode & S_IFMT) {
case S_IFREG: nreg++; break;
case S_IFBLK: nblk++; break;
case S_IFCHR: nchr++; break;
case S_IFIFO: nfifo++;break;
case S_IFLNK: nslink++; break;
case S_IFSOCK: nsock++; break;
case S_IFDIR:
err_dump("for S_IFDIR for %s",pathname);
}
break;
// 目录
case FTW_D:
ndir++;
break;
// 无法读取的目录
case FTW_DNR:
err_ret("can't read directory %s",pathname);
break;
// 无法读取状态
case FTW_NS:
// 出错情况打印一下
err_ret("stat error for %s",pathname);
break;
default:
err_dump("unknown type %d for pathname %s",type,pathname);
}
return (0);
}

课后练习

4.1 stat跟随符号链接,lstat不跟随符号链接

4.2 umask 777则所有位被关闭,文件任何权限都没有

4.6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 首先创建一个空洞文件
#include <bits/stdc++.h>
#include <unistd.h>
#include <fcntl.h>
#include "apue.h"

int main(int argc,char *argv[]) {
std::string path = "/home/luchao/Documents/Apue/4/1.in";
umask(0);
int fd = open(path.c_str(),O_CREAT|O_RDWR,0644);
if (fd == -1)
err_sys("open errro");
std::string content("this is write content");
if (write(fd,content.c_str(),content.size()) != content.size())
err_sys("write error");
// 重新定位形成空洞
int offset = lseek(fd,1024,SEEK_SET);
if (offset != 1024)
err_sys("offset error");
content = "other content";
if (write(fd,content.c_str(),content.size()) != content.size())
err_sys("write second error");

close(fd);
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// 复制空洞文件
#include <bits/stdc++.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>

const int maxn = 1024;
char buf[maxn];

int main(int argc,char *argv[]) {
if (argc != 3) {
std::cerr << "Usage: ./a.out file1 file2\n";
exit(1);
}
int fd1,fd2;
if ((fd1 = open(argv[1],O_RDWR)) == -1) {
std::cerr << "open " << argv[1] << " failed\n";
exit(1);
}
// 设置文件掩码
umask(0);
if ((fd2 = open(argv[2],O_RDWR|O_CREAT,0644)) == -1) {
std::cerr << "open " << argv[1] << " failed\n";
exit(1);
}

long long out_offset = 0;
int cnt,start_offset;
// =0是正是内容,!=0 是空洞
bool flag;
while (cnt = read(fd1,buf,maxn)) {
flag = 0,start_offset = 0;
for (int i=0; i<cnt; ++i) {
if (!flag && buf[i]!=0) {
flag = 1,start_offset = i;
if (lseek(fd2,out_offset + start_offset,SEEK_SET) == -1) {
std::cerr << "lseek failed\n";
exit(1);
}
}
// 空洞出现
else if (flag && buf[i] == 0) {
// 写入
if (write(fd2,buf + start_offset,i - start_offset) == -1) {
std::cerr << "fd2 write failed\n";
exit(1);
}
flag = 0;
}
}
// 最后一次未复制的复制过去
if (flag)
if (write(fd2,buf + start_offset,cnt - start_offset) == -1) {
std::cerr << "fd2 write failed\n";
exit(1);
}
out_offset += cnt;
}
return 0;
}
  1. 16 UNIX对目录树深度无要求,但是对路径长度有要求PATH_MAX限制,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <bits/stdc++.h>
#include "apue.h"
#include <fcntl.h>
#include "path_alloc.h"

#define DEPTH 1000
#define STARTDIR "/tmp"
#define NAME "alonglonglonglonglonglonglonglonglonglongname"
#define MAXSZ (10*8192)

int main(int argc,char *argv[]) {
if(chdir(STARTDIR) < 0)
err_sys("chdir error");
for (int i=0; i<DEPTH; ++i) {
if (mkdir(NAME,DIR_MODE) < 0)
err_sys("mkdir failed, i = %d",i);
// /tmp/aln/aln/aln/aln
if (chdir(NAME) < 0)
err_sys("create error");
}
// 走到这一步说明已经递归创建出了1000深度目录路径
if (creat("afile",FILE_MODE) < 0)
err_sys("creat error");
size_t size;
char *path;
path = (char *)path_alloc(&size);
for (;;) {
// 大小够了,可以得到路径
if(getcwd(path,size) != NULL)
break;
// 否则一直扩大分配内存
else {
err_ret("getcwd failed,size = %ld",(long)size);
size += 1000;
if (size > MAXSZ)
err_quit("giving up");
if ((path = (char *)realloc(path,size)) == NULL)
err_sys("realloc error");
}
}
printf("length = %ld\n%s\n",(long)strlen(path),path);
return 0;
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!