This repository has been archived by the owner on Sep 1, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinit_conf_map.c
230 lines (172 loc) · 5.71 KB
/
init_conf_map.c
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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#include "init.h"
#include "init_conf.h"
/* Init uses mmap to allocate memory *and* to read files.
At any given point, there are at most three mmaped areas: */
struct cblock cfgblock; /* active configuration */
struct nblock newblock; /* newly compiled configuration */
struct fblock fileblock; /* the file being parsed */
/* New configuration is built by appending structures of known size
to newblock. If reconfiguration succeeds, the blocks are exchanged:
old cfgblock gets unmmaped and newblock becomes cfgblock.
In case configuration fails, we just unmmap newblock. */
int mmapblock(int size)
{
if(size > PAGESIZE)
return -1;
if(newblock.addr) {
if(newblock.len < size)
return -1;
} else {
const int prot = PROT_READ | PROT_WRITE;
const int flags = MAP_PRIVATE | MAP_ANONYMOUS;
void* addr = mmap(NULL, PAGESIZE, prot, flags, -1, 0);
if(addr == MAP_FAILED)
return -1;
newblock.addr = addr;
newblock.len = PAGESIZE;
}
memset(newblock.addr, 0, newblock.len);
newblock.ptr = size;
return 0;
}
#ifdef NOMMU
/* On non-MMU targets, MREMAP_MAYMOVE does not really work,
so we've got to emulate it explicitly. */
static void* mremapnommu(void* oldaddr, size_t oldsize, size_t newsize, int flags)
{
void* newaddr = mremap(oldaddr, oldsize, newsize, flags);
if(newaddr != MAP_FAILED)
return newaddr;
const int prot = PROT_READ | PROT_WRITE;
const int flags = MAP_PRIVATE | MAP_ANONYMOUS;
newaddr = mmap(NULL, newsize, prot, flags, -1, 0);
if(newaddr == MAP_FAILED)
return newaddr;
memcpy(newaddr, oldaddr, oldsize);
munmap(oldaddr, oldsize);
return newaddr;
}
#define mremap(oa, os, ns, fl) mremapnommu(oa, os, ns, fl)
#endif
/* newblock.ptr tracks the start of empty space in newblock.
We make sure there is enough space, and allocate it by moving ptr over. */
offset extendblock(int size)
{
int ret = newblock.ptr;
int rem = (newblock.len - newblock.ptr); /* space remaining */
int add = size - rem; /* space to be added */
if(add <= 0)
goto moveptr;
if(add % PAGESIZE)
add += (PAGESIZE - add % PAGESIZE);
int oldlen = newblock.len;
int newlen = oldlen + add;
if(newlen < oldlen)
return -1; /* overflow? */
void* newaddr = mremap(newblock.addr, oldlen, newlen, MREMAP_MAYMOVE);
if(newaddr == MAP_FAILED)
return -1;
newblock.addr = newaddr;
newblock.len = newlen;
moveptr:
newblock.ptr += size;
return ret;
}
void munmapblock(void)
{
munmap(newblock.addr, newblock.len);
newblock.addr = 0;
newblock.len = 0;
};
/* Normally the old block in unmmaped and the new one takes its place.
However, cfgblock may happen be empty on the first reconfig attempt
if the old config is in fact builtin config. */
void exchangeblocks(void)
{
if(cfgblock.addr)
munmap(cfgblock.addr, cfgblock.len);
cfgblock.addr = newblock.addr;
cfgblock.len = newblock.len;
newblock.addr = NULL;
newblock.len = 0;
};
/* The file block is used to parse inittab or service files linewise.
Filename is stored in the structure for error reporting.
The pointer must be usable up until munmapfile is called.
The block is initially allocated to hold struct config and
struct scratch. Initrecs are added later with extendblock.
The only values of size ever used are sizeof(struct config) +
sizeof(struct scratch) in init_conf.c and 0 in tests, so there
is little point in check them.
There is a relatively unlikely case when a new reconfigure request
comes before newblock from the previous one is moved over to cfgblock.
In such a case, we re-use newblock without unmmaping it.
Again, it is always large enough, so the check is really redundant.
Due to average inittab being about 1-2k, it is always read whole;
for service files, only the head is mmaped.
Init makes no distinction between mmap failure and open failure,
both mean the new inittab won't be used.
maxlen > 0: error if the file is larger (inittab)
maxlen < 0: map at most -maxlen first bytes (initdir)
The result is always 0-terminated. */
int mmapfile(const char* filename, int maxlen)
{
struct stat st;
int fd = open(filename, O_RDONLY);
if(fd < 0)
retwarn(-1, "can't open %s: %m", filename);
if(fstat(fd, &st) < 0)
gotowarn(out, "can't stat %s: %m", filename);
if(!S_ISREG(st.st_mode))
gotowarn(out, "%s: not a regular file", filename);
if(maxlen < 0 && st.st_size > -maxlen)
/* Because ints are used in lots of places,
it is a good idea to avoid loading anything that
exceeds 2^31 when compiled into newblock */
gotowarn(out, "%s: file too large", filename);
int stm = st.st_size;
if(maxlen > 0 && stm > maxlen) stm = maxlen;
/* with one guard byte at the end, to hold \0 */
const int prot = PROT_READ | PROT_WRITE;
const int flags = MAP_PRIVATE;
char* addr = mmap(NULL, stm + 1, prot, flags, fd, 0);
if(addr == MAP_FAILED)
gotowarn(out, "%s: mmap failed: %m", filename);
addr[stm] = '\0';
fileblock.buf = addr;
fileblock.len = stm;
fileblock.ls = NULL;
fileblock.le = NULL;
fileblock.name = filename;
fileblock.line = 0;
close(fd);
return 0;
out: close(fd);
return -1;
}
int munmapfile(void)
{
return munmap(fileblock.buf, fileblock.len);
}
/* The file is mmaped rw and private. We place the pointers
and terminate the line with \0, overwriting \n. */
char* nextline(void)
{
char* le = fileblock.le;
char* ls = le ? le + 1 : fileblock.buf;
char* end = fileblock.buf + fileblock.len;
if(ls >= end) return NULL;
for(le = ls; le < end && *le != '\n'; le++)
; /* clang is full of hatred towards elegant concise expressions */
*le = '\0';
fileblock.ls = ls;
fileblock.le = le;
fileblock.line++;
return ls;
}