Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/um/os-Linux/umid.c
10817 views
1
/*
2
* Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
3
* Licensed under the GPL
4
*/
5
6
#include <stdio.h>
7
#include <stdlib.h>
8
#include <dirent.h>
9
#include <errno.h>
10
#include <fcntl.h>
11
#include <signal.h>
12
#include <string.h>
13
#include <unistd.h>
14
#include <sys/stat.h>
15
#include "init.h"
16
#include "kern_constants.h"
17
#include "os.h"
18
#include "user.h"
19
20
#define UML_DIR "~/.uml/"
21
22
#define UMID_LEN 64
23
24
/* Changed by set_umid, which is run early in boot */
25
static char umid[UMID_LEN] = { 0 };
26
27
/* Changed by set_uml_dir and make_uml_dir, which are run early in boot */
28
static char *uml_dir = UML_DIR;
29
30
static int __init make_uml_dir(void)
31
{
32
char dir[512] = { '\0' };
33
int len, err;
34
35
if (*uml_dir == '~') {
36
char *home = getenv("HOME");
37
38
err = -ENOENT;
39
if (home == NULL) {
40
printk(UM_KERN_ERR "make_uml_dir : no value in "
41
"environment for $HOME\n");
42
goto err;
43
}
44
strlcpy(dir, home, sizeof(dir));
45
uml_dir++;
46
}
47
strlcat(dir, uml_dir, sizeof(dir));
48
len = strlen(dir);
49
if (len > 0 && dir[len - 1] != '/')
50
strlcat(dir, "/", sizeof(dir));
51
52
err = -ENOMEM;
53
uml_dir = malloc(strlen(dir) + 1);
54
if (uml_dir == NULL) {
55
printf("make_uml_dir : malloc failed, errno = %d\n", errno);
56
goto err;
57
}
58
strcpy(uml_dir, dir);
59
60
if ((mkdir(uml_dir, 0777) < 0) && (errno != EEXIST)) {
61
printf("Failed to mkdir '%s': %s\n", uml_dir, strerror(errno));
62
err = -errno;
63
goto err_free;
64
}
65
return 0;
66
67
err_free:
68
free(uml_dir);
69
err:
70
uml_dir = NULL;
71
return err;
72
}
73
74
/*
75
* Unlinks the files contained in @dir and then removes @dir.
76
* Doesn't handle directory trees, so it's not like rm -rf, but almost such. We
77
* ignore ENOENT errors for anything (they happen, strangely enough - possibly
78
* due to races between multiple dying UML threads).
79
*/
80
static int remove_files_and_dir(char *dir)
81
{
82
DIR *directory;
83
struct dirent *ent;
84
int len;
85
char file[256];
86
int ret;
87
88
directory = opendir(dir);
89
if (directory == NULL) {
90
if (errno != ENOENT)
91
return -errno;
92
else
93
return 0;
94
}
95
96
while ((ent = readdir(directory)) != NULL) {
97
if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
98
continue;
99
len = strlen(dir) + sizeof("/") + strlen(ent->d_name) + 1;
100
if (len > sizeof(file)) {
101
ret = -E2BIG;
102
goto out;
103
}
104
105
sprintf(file, "%s/%s", dir, ent->d_name);
106
if (unlink(file) < 0 && errno != ENOENT) {
107
ret = -errno;
108
goto out;
109
}
110
}
111
112
if (rmdir(dir) < 0 && errno != ENOENT) {
113
ret = -errno;
114
goto out;
115
}
116
117
ret = 0;
118
out:
119
closedir(directory);
120
return ret;
121
}
122
123
/*
124
* This says that there isn't already a user of the specified directory even if
125
* there are errors during the checking. This is because if these errors
126
* happen, the directory is unusable by the pre-existing UML, so we might as
127
* well take it over. This could happen either by
128
* the existing UML somehow corrupting its umid directory
129
* something other than UML sticking stuff in the directory
130
* this boot racing with a shutdown of the other UML
131
* In any of these cases, the directory isn't useful for anything else.
132
*
133
* Boolean return: 1 if in use, 0 otherwise.
134
*/
135
static inline int is_umdir_used(char *dir)
136
{
137
char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")];
138
char pid[sizeof("nnnnn\0")], *end;
139
int dead, fd, p, n, err;
140
141
n = snprintf(file, sizeof(file), "%s/pid", dir);
142
if (n >= sizeof(file)) {
143
printk(UM_KERN_ERR "is_umdir_used - pid filename too long\n");
144
err = -E2BIG;
145
goto out;
146
}
147
148
dead = 0;
149
fd = open(file, O_RDONLY);
150
if (fd < 0) {
151
fd = -errno;
152
if (fd != -ENOENT) {
153
printk(UM_KERN_ERR "is_umdir_used : couldn't open pid "
154
"file '%s', err = %d\n", file, -fd);
155
}
156
goto out;
157
}
158
159
err = 0;
160
n = read(fd, pid, sizeof(pid));
161
if (n < 0) {
162
printk(UM_KERN_ERR "is_umdir_used : couldn't read pid file "
163
"'%s', err = %d\n", file, errno);
164
goto out_close;
165
} else if (n == 0) {
166
printk(UM_KERN_ERR "is_umdir_used : couldn't read pid file "
167
"'%s', 0-byte read\n", file);
168
goto out_close;
169
}
170
171
p = strtoul(pid, &end, 0);
172
if (end == pid) {
173
printk(UM_KERN_ERR "is_umdir_used : couldn't parse pid file "
174
"'%s', errno = %d\n", file, errno);
175
goto out_close;
176
}
177
178
if ((kill(p, 0) == 0) || (errno != ESRCH)) {
179
printk(UM_KERN_ERR "umid \"%s\" is already in use by pid %d\n",
180
umid, p);
181
return 1;
182
}
183
184
out_close:
185
close(fd);
186
out:
187
return 0;
188
}
189
190
/*
191
* Try to remove the directory @dir unless it's in use.
192
* Precondition: @dir exists.
193
* Returns 0 for success, < 0 for failure in removal or if the directory is in
194
* use.
195
*/
196
static int umdir_take_if_dead(char *dir)
197
{
198
int ret;
199
if (is_umdir_used(dir))
200
return -EEXIST;
201
202
ret = remove_files_and_dir(dir);
203
if (ret) {
204
printk(UM_KERN_ERR "is_umdir_used - remove_files_and_dir "
205
"failed with err = %d\n", ret);
206
}
207
return ret;
208
}
209
210
static void __init create_pid_file(void)
211
{
212
char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")];
213
char pid[sizeof("nnnnn\0")];
214
int fd, n;
215
216
if (umid_file_name("pid", file, sizeof(file)))
217
return;
218
219
fd = open(file, O_RDWR | O_CREAT | O_EXCL, 0644);
220
if (fd < 0) {
221
printk(UM_KERN_ERR "Open of machine pid file \"%s\" failed: "
222
"%s\n", file, strerror(errno));
223
return;
224
}
225
226
snprintf(pid, sizeof(pid), "%d\n", getpid());
227
n = write(fd, pid, strlen(pid));
228
if (n != strlen(pid))
229
printk(UM_KERN_ERR "Write of pid file failed - err = %d\n",
230
errno);
231
232
close(fd);
233
}
234
235
int __init set_umid(char *name)
236
{
237
if (strlen(name) > UMID_LEN - 1)
238
return -E2BIG;
239
240
strlcpy(umid, name, sizeof(umid));
241
242
return 0;
243
}
244
245
/* Changed in make_umid, which is called during early boot */
246
static int umid_setup = 0;
247
248
static int __init make_umid(void)
249
{
250
int fd, err;
251
char tmp[256];
252
253
if (umid_setup)
254
return 0;
255
256
make_uml_dir();
257
258
if (*umid == '\0') {
259
strlcpy(tmp, uml_dir, sizeof(tmp));
260
strlcat(tmp, "XXXXXX", sizeof(tmp));
261
fd = mkstemp(tmp);
262
if (fd < 0) {
263
printk(UM_KERN_ERR "make_umid - mkstemp(%s) failed: "
264
"%s\n", tmp, strerror(errno));
265
err = -errno;
266
goto err;
267
}
268
269
close(fd);
270
271
set_umid(&tmp[strlen(uml_dir)]);
272
273
/*
274
* There's a nice tiny little race between this unlink and
275
* the mkdir below. It'd be nice if there were a mkstemp
276
* for directories.
277
*/
278
if (unlink(tmp)) {
279
err = -errno;
280
goto err;
281
}
282
}
283
284
snprintf(tmp, sizeof(tmp), "%s%s", uml_dir, umid);
285
err = mkdir(tmp, 0777);
286
if (err < 0) {
287
err = -errno;
288
if (err != -EEXIST)
289
goto err;
290
291
if (umdir_take_if_dead(tmp) < 0)
292
goto err;
293
294
err = mkdir(tmp, 0777);
295
}
296
if (err) {
297
err = -errno;
298
printk(UM_KERN_ERR "Failed to create '%s' - err = %d\n", umid,
299
errno);
300
goto err;
301
}
302
303
umid_setup = 1;
304
305
create_pid_file();
306
307
err = 0;
308
err:
309
return err;
310
}
311
312
static int __init make_umid_init(void)
313
{
314
if (!make_umid())
315
return 0;
316
317
/*
318
* If initializing with the given umid failed, then try again with
319
* a random one.
320
*/
321
printk(UM_KERN_ERR "Failed to initialize umid \"%s\", trying with a "
322
"random umid\n", umid);
323
*umid = '\0';
324
make_umid();
325
326
return 0;
327
}
328
329
__initcall(make_umid_init);
330
331
int __init umid_file_name(char *name, char *buf, int len)
332
{
333
int n, err;
334
335
err = make_umid();
336
if (err)
337
return err;
338
339
n = snprintf(buf, len, "%s%s/%s", uml_dir, umid, name);
340
if (n >= len) {
341
printk(UM_KERN_ERR "umid_file_name : buffer too short\n");
342
return -E2BIG;
343
}
344
345
return 0;
346
}
347
348
char *get_umid(void)
349
{
350
return umid;
351
}
352
353
static int __init set_uml_dir(char *name, int *add)
354
{
355
if (*name == '\0') {
356
printf("uml_dir can't be an empty string\n");
357
return 0;
358
}
359
360
if (name[strlen(name) - 1] == '/') {
361
uml_dir = name;
362
return 0;
363
}
364
365
uml_dir = malloc(strlen(name) + 2);
366
if (uml_dir == NULL) {
367
printf("Failed to malloc uml_dir - error = %d\n", errno);
368
369
/*
370
* Return 0 here because do_initcalls doesn't look at
371
* the return value.
372
*/
373
return 0;
374
}
375
sprintf(uml_dir, "%s/", name);
376
377
return 0;
378
}
379
380
__uml_setup("uml_dir=", set_uml_dir,
381
"uml_dir=<directory>\n"
382
" The location to place the pid and umid files.\n\n"
383
);
384
385
static void remove_umid_dir(void)
386
{
387
char dir[strlen(uml_dir) + UMID_LEN + 1], err;
388
389
sprintf(dir, "%s%s", uml_dir, umid);
390
err = remove_files_and_dir(dir);
391
if (err)
392
printf("remove_umid_dir - remove_files_and_dir failed with "
393
"err = %d\n", err);
394
}
395
396
__uml_exitcall(remove_umid_dir);
397
398