blob: c1dc89261f678451e9428c63b63078cc706a6cf5 [file] [log] [blame]
Jeff Dikeba180fd2007-10-16 01:27:00 -07001/*
2 * Copyright (C) 2002 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
3 * Licensed under the GPL
4 */
5
Jeff Dike2264c4752006-01-06 00:18:59 -08006#include <stdio.h>
Jeff Dike2264c4752006-01-06 00:18:59 -08007#include <stdlib.h>
Jeff Dike2264c4752006-01-06 00:18:59 -08008#include <dirent.h>
Jeff Dikeba180fd2007-10-16 01:27:00 -07009#include <errno.h>
10#include <fcntl.h>
11#include <signal.h>
12#include <string.h>
13#include <unistd.h>
Jeff Dike2264c4752006-01-06 00:18:59 -080014#include <sys/stat.h>
Al Viro37185b32012-10-08 03:27:32 +010015#include <init.h>
16#include <os.h>
Jeff Dike2264c4752006-01-06 00:18:59 -080017
18#define UML_DIR "~/.uml/"
19
20#define UMID_LEN 64
21
22/* Changed by set_umid, which is run early in boot */
Jeff Dikede5fe762007-02-10 01:44:25 -080023static char umid[UMID_LEN] = { 0 };
Jeff Dike2264c4752006-01-06 00:18:59 -080024
25/* Changed by set_uml_dir and make_uml_dir, which are run early in boot */
26static char *uml_dir = UML_DIR;
27
28static int __init make_uml_dir(void)
29{
30 char dir[512] = { '\0' };
Jeff Dike7eebe8a2006-01-06 00:19:01 -080031 int len, err;
Jeff Dike2264c4752006-01-06 00:18:59 -080032
Jeff Dikeba180fd2007-10-16 01:27:00 -070033 if (*uml_dir == '~') {
Jeff Dike2264c4752006-01-06 00:18:59 -080034 char *home = getenv("HOME");
35
Jeff Dike7eebe8a2006-01-06 00:19:01 -080036 err = -ENOENT;
Jeff Dikeba180fd2007-10-16 01:27:00 -070037 if (home == NULL) {
38 printk(UM_KERN_ERR "make_uml_dir : no value in "
39 "environment for $HOME\n");
Jeff Dike7eebe8a2006-01-06 00:19:01 -080040 goto err;
Jeff Dike2264c4752006-01-06 00:18:59 -080041 }
42 strlcpy(dir, home, sizeof(dir));
43 uml_dir++;
44 }
45 strlcat(dir, uml_dir, sizeof(dir));
46 len = strlen(dir);
47 if (len > 0 && dir[len - 1] != '/')
48 strlcat(dir, "/", sizeof(dir));
49
Jeff Dike7eebe8a2006-01-06 00:19:01 -080050 err = -ENOMEM;
Jeff Dike2264c4752006-01-06 00:18:59 -080051 uml_dir = malloc(strlen(dir) + 1);
52 if (uml_dir == NULL) {
53 printf("make_uml_dir : malloc failed, errno = %d\n", errno);
Jeff Dike7eebe8a2006-01-06 00:19:01 -080054 goto err;
Jeff Dike2264c4752006-01-06 00:18:59 -080055 }
56 strcpy(uml_dir, dir);
57
Jeff Dikeba180fd2007-10-16 01:27:00 -070058 if ((mkdir(uml_dir, 0777) < 0) && (errno != EEXIST)) {
Jeff Dike2264c4752006-01-06 00:18:59 -080059 printf("Failed to mkdir '%s': %s\n", uml_dir, strerror(errno));
Jeff Dike7eebe8a2006-01-06 00:19:01 -080060 err = -errno;
61 goto err_free;
Jeff Dike2264c4752006-01-06 00:18:59 -080062 }
63 return 0;
Jeff Dike7eebe8a2006-01-06 00:19:01 -080064
65err_free:
66 free(uml_dir);
67err:
68 uml_dir = NULL;
69 return err;
Jeff Dike2264c4752006-01-06 00:18:59 -080070}
71
Paolo 'Blaisorblade' Giarrussoeb289312006-07-01 04:36:24 -070072/*
73 * Unlinks the files contained in @dir and then removes @dir.
74 * Doesn't handle directory trees, so it's not like rm -rf, but almost such. We
Jeff Dikeba180fd2007-10-16 01:27:00 -070075 * ignore ENOENT errors for anything (they happen, strangely enough - possibly
76 * due to races between multiple dying UML threads).
Paolo 'Blaisorblade' Giarrussoeb289312006-07-01 04:36:24 -070077 */
78static int remove_files_and_dir(char *dir)
Jeff Dike2264c4752006-01-06 00:18:59 -080079{
80 DIR *directory;
81 struct dirent *ent;
82 int len;
83 char file[256];
Paolo 'Blaisorblade' Giarrussoeb289312006-07-01 04:36:24 -070084 int ret;
Jeff Dike2264c4752006-01-06 00:18:59 -080085
86 directory = opendir(dir);
Paolo 'Blaisorblade' Giarrussoeb289312006-07-01 04:36:24 -070087 if (directory == NULL) {
88 if (errno != ENOENT)
89 return -errno;
90 else
91 return 0;
92 }
Jeff Dike7eebe8a2006-01-06 00:19:01 -080093
Paolo 'Blaisorblade' Giarrussoeb289312006-07-01 04:36:24 -070094 while ((ent = readdir(directory)) != NULL) {
95 if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, ".."))
Jeff Dike2264c4752006-01-06 00:18:59 -080096 continue;
97 len = strlen(dir) + sizeof("/") + strlen(ent->d_name) + 1;
Paolo 'Blaisorblade' Giarrussoeb289312006-07-01 04:36:24 -070098 if (len > sizeof(file)) {
99 ret = -E2BIG;
100 goto out;
101 }
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800102
Jeff Dike2264c4752006-01-06 00:18:59 -0800103 sprintf(file, "%s/%s", dir, ent->d_name);
Paolo 'Blaisorblade' Giarrussoeb289312006-07-01 04:36:24 -0700104 if (unlink(file) < 0 && errno != ENOENT) {
105 ret = -errno;
106 goto out;
107 }
Jeff Dike2264c4752006-01-06 00:18:59 -0800108 }
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800109
Paolo 'Blaisorblade' Giarrussoeb289312006-07-01 04:36:24 -0700110 if (rmdir(dir) < 0 && errno != ENOENT) {
111 ret = -errno;
112 goto out;
113 }
114
115 ret = 0;
116out:
117 closedir(directory);
118 return ret;
Jeff Dike2264c4752006-01-06 00:18:59 -0800119}
120
Jeff Dikeba180fd2007-10-16 01:27:00 -0700121/*
122 * This says that there isn't already a user of the specified directory even if
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800123 * there are errors during the checking. This is because if these errors
124 * happen, the directory is unusable by the pre-existing UML, so we might as
125 * well take it over. This could happen either by
126 * the existing UML somehow corrupting its umid directory
127 * something other than UML sticking stuff in the directory
128 * this boot racing with a shutdown of the other UML
129 * In any of these cases, the directory isn't useful for anything else.
Paolo 'Blaisorblade' Giarrusso912ad922006-07-01 04:36:23 -0700130 *
131 * Boolean return: 1 if in use, 0 otherwise.
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800132 */
Paolo 'Blaisorblade' Giarrusso912ad922006-07-01 04:36:23 -0700133static inline int is_umdir_used(char *dir)
Jeff Dike2264c4752006-01-06 00:18:59 -0800134{
135 char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")];
136 char pid[sizeof("nnnnn\0")], *end;
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800137 int dead, fd, p, n, err;
Jeff Dike2264c4752006-01-06 00:18:59 -0800138
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800139 n = snprintf(file, sizeof(file), "%s/pid", dir);
Jeff Dikeba180fd2007-10-16 01:27:00 -0700140 if (n >= sizeof(file)) {
141 printk(UM_KERN_ERR "is_umdir_used - pid filename too long\n");
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800142 err = -E2BIG;
143 goto out;
144 }
145
Jeff Dike2264c4752006-01-06 00:18:59 -0800146 dead = 0;
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800147 fd = open(file, O_RDONLY);
Jeff Dikeba180fd2007-10-16 01:27:00 -0700148 if (fd < 0) {
Paolo 'Blaisorblade' Giarrussod84a19c2006-04-10 22:53:38 -0700149 fd = -errno;
Jeff Dikeba180fd2007-10-16 01:27:00 -0700150 if (fd != -ENOENT) {
151 printk(UM_KERN_ERR "is_umdir_used : couldn't open pid "
152 "file '%s', err = %d\n", file, -fd);
Jeff Dike2264c4752006-01-06 00:18:59 -0800153 }
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800154 goto out;
Jeff Dike2264c4752006-01-06 00:18:59 -0800155 }
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800156
157 err = 0;
158 n = read(fd, pid, sizeof(pid));
Jeff Dikeba180fd2007-10-16 01:27:00 -0700159 if (n < 0) {
160 printk(UM_KERN_ERR "is_umdir_used : couldn't read pid file "
161 "'%s', err = %d\n", file, errno);
Paolo 'Blaisorblade' Giarrussod84a19c2006-04-10 22:53:38 -0700162 goto out_close;
Jeff Dikeba180fd2007-10-16 01:27:00 -0700163 } else if (n == 0) {
164 printk(UM_KERN_ERR "is_umdir_used : couldn't read pid file "
165 "'%s', 0-byte read\n", file);
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800166 goto out_close;
Jeff Dike2264c4752006-01-06 00:18:59 -0800167 }
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800168
169 p = strtoul(pid, &end, 0);
Jeff Dikeba180fd2007-10-16 01:27:00 -0700170 if (end == pid) {
171 printk(UM_KERN_ERR "is_umdir_used : couldn't parse pid file "
172 "'%s', errno = %d\n", file, errno);
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800173 goto out_close;
174 }
175
Jeff Dikeba180fd2007-10-16 01:27:00 -0700176 if ((kill(p, 0) == 0) || (errno != ESRCH)) {
177 printk(UM_KERN_ERR "umid \"%s\" is already in use by pid %d\n",
178 umid, p);
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800179 return 1;
Jeff Dike1fbbd6842006-03-27 01:14:39 -0800180 }
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800181
Paolo 'Blaisorblade' Giarrussod84a19c2006-04-10 22:53:38 -0700182out_close:
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800183 close(fd);
Paolo 'Blaisorblade' Giarrussod84a19c2006-04-10 22:53:38 -0700184out:
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800185 return 0;
Jeff Dike2264c4752006-01-06 00:18:59 -0800186}
187
Paolo 'Blaisorblade' Giarrusso912ad922006-07-01 04:36:23 -0700188/*
189 * Try to remove the directory @dir unless it's in use.
190 * Precondition: @dir exists.
191 * Returns 0 for success, < 0 for failure in removal or if the directory is in
192 * use.
193 */
194static int umdir_take_if_dead(char *dir)
195{
196 int ret;
197 if (is_umdir_used(dir))
198 return -EEXIST;
199
Paolo 'Blaisorblade' Giarrussoeb289312006-07-01 04:36:24 -0700200 ret = remove_files_and_dir(dir);
Paolo 'Blaisorblade' Giarrusso912ad922006-07-01 04:36:23 -0700201 if (ret) {
Jeff Dikeba180fd2007-10-16 01:27:00 -0700202 printk(UM_KERN_ERR "is_umdir_used - remove_files_and_dir "
203 "failed with err = %d\n", ret);
Paolo 'Blaisorblade' Giarrusso912ad922006-07-01 04:36:23 -0700204 }
205 return ret;
206}
207
Jeff Dike2264c4752006-01-06 00:18:59 -0800208static void __init create_pid_file(void)
209{
210 char file[strlen(uml_dir) + UMID_LEN + sizeof("/pid\0")];
211 char pid[sizeof("nnnnn\0")];
212 int fd, n;
213
Jeff Dikeba180fd2007-10-16 01:27:00 -0700214 if (umid_file_name("pid", file, sizeof(file)))
Jeff Dike2264c4752006-01-06 00:18:59 -0800215 return;
216
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800217 fd = open(file, O_RDWR | O_CREAT | O_EXCL, 0644);
Jeff Dikeba180fd2007-10-16 01:27:00 -0700218 if (fd < 0) {
219 printk(UM_KERN_ERR "Open of machine pid file \"%s\" failed: "
220 "%s\n", file, strerror(errno));
Jeff Dike2264c4752006-01-06 00:18:59 -0800221 return;
222 }
223
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800224 snprintf(pid, sizeof(pid), "%d\n", getpid());
225 n = write(fd, pid, strlen(pid));
Jeff Dikeba180fd2007-10-16 01:27:00 -0700226 if (n != strlen(pid))
227 printk(UM_KERN_ERR "Write of pid file failed - err = %d\n",
228 errno);
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800229
230 close(fd);
Jeff Dike2264c4752006-01-06 00:18:59 -0800231}
232
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800233int __init set_umid(char *name)
Jeff Dike2264c4752006-01-06 00:18:59 -0800234{
Jeff Dikeba180fd2007-10-16 01:27:00 -0700235 if (strlen(name) > UMID_LEN - 1)
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800236 return -E2BIG;
237
Jeff Dike2264c4752006-01-06 00:18:59 -0800238 strlcpy(umid, name, sizeof(umid));
239
240 return 0;
241}
242
Jeff Dikede5fe762007-02-10 01:44:25 -0800243/* Changed in make_umid, which is called during early boot */
Jeff Dike2264c4752006-01-06 00:18:59 -0800244static int umid_setup = 0;
245
WANG Cong99764fa2008-07-23 21:28:49 -0700246static int __init make_umid(void)
Jeff Dike2264c4752006-01-06 00:18:59 -0800247{
248 int fd, err;
249 char tmp[256];
250
Jeff Dikeba180fd2007-10-16 01:27:00 -0700251 if (umid_setup)
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800252 return 0;
253
Jeff Dike2264c4752006-01-06 00:18:59 -0800254 make_uml_dir();
255
Jeff Dikeba180fd2007-10-16 01:27:00 -0700256 if (*umid == '\0') {
Jeff Dike2264c4752006-01-06 00:18:59 -0800257 strlcpy(tmp, uml_dir, sizeof(tmp));
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800258 strlcat(tmp, "XXXXXX", sizeof(tmp));
Jeff Dike2264c4752006-01-06 00:18:59 -0800259 fd = mkstemp(tmp);
Jeff Dikeba180fd2007-10-16 01:27:00 -0700260 if (fd < 0) {
261 printk(UM_KERN_ERR "make_umid - mkstemp(%s) failed: "
262 "%s\n", tmp, strerror(errno));
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800263 err = -errno;
264 goto err;
Jeff Dike2264c4752006-01-06 00:18:59 -0800265 }
266
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800267 close(fd);
268
269 set_umid(&tmp[strlen(uml_dir)]);
270
Jeff Dikeba180fd2007-10-16 01:27:00 -0700271 /*
272 * There's a nice tiny little race between this unlink and
Jeff Dike2264c4752006-01-06 00:18:59 -0800273 * the mkdir below. It'd be nice if there were a mkstemp
274 * for directories.
275 */
Jeff Dikeba180fd2007-10-16 01:27:00 -0700276 if (unlink(tmp)) {
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800277 err = -errno;
278 goto err;
Jeff Dike2264c4752006-01-06 00:18:59 -0800279 }
280 }
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800281
282 snprintf(tmp, sizeof(tmp), "%s%s", uml_dir, umid);
283 err = mkdir(tmp, 0777);
Jeff Dikeba180fd2007-10-16 01:27:00 -0700284 if (err < 0) {
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800285 err = -errno;
Jeff Dikeba180fd2007-10-16 01:27:00 -0700286 if (err != -EEXIST)
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800287 goto err;
288
Paolo 'Blaisorblade' Giarrusso912ad922006-07-01 04:36:23 -0700289 if (umdir_take_if_dead(tmp) < 0)
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800290 goto err;
291
292 err = mkdir(tmp, 0777);
293 }
Jeff Dikeba180fd2007-10-16 01:27:00 -0700294 if (err) {
Jeff Dike1fbbd6842006-03-27 01:14:39 -0800295 err = -errno;
Jeff Dikeba180fd2007-10-16 01:27:00 -0700296 printk(UM_KERN_ERR "Failed to create '%s' - err = %d\n", umid,
297 errno);
Jeff Dike1fbbd6842006-03-27 01:14:39 -0800298 goto err;
Jeff Dike2264c4752006-01-06 00:18:59 -0800299 }
300
301 umid_setup = 1;
302
303 create_pid_file();
304
Jeff Dike1fbbd6842006-03-27 01:14:39 -0800305 err = 0;
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800306 err:
307 return err;
Jeff Dike2264c4752006-01-06 00:18:59 -0800308}
309
310static int __init make_umid_init(void)
311{
Jeff Dikeba180fd2007-10-16 01:27:00 -0700312 if (!make_umid())
Jeff Dike1fbbd6842006-03-27 01:14:39 -0800313 return 0;
314
Jeff Dikeba180fd2007-10-16 01:27:00 -0700315 /*
316 * If initializing with the given umid failed, then try again with
Jeff Dike1fbbd6842006-03-27 01:14:39 -0800317 * a random one.
318 */
Jeff Dikeba180fd2007-10-16 01:27:00 -0700319 printk(UM_KERN_ERR "Failed to initialize umid \"%s\", trying with a "
320 "random umid\n", umid);
Jeff Dike1fbbd6842006-03-27 01:14:39 -0800321 *umid = '\0';
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800322 make_umid();
Jeff Dike2264c4752006-01-06 00:18:59 -0800323
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800324 return 0;
Jeff Dike2264c4752006-01-06 00:18:59 -0800325}
326
327__initcall(make_umid_init);
328
329int __init umid_file_name(char *name, char *buf, int len)
330{
331 int n, err;
332
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800333 err = make_umid();
Jeff Dikeba180fd2007-10-16 01:27:00 -0700334 if (err)
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800335 return err;
Jeff Dike2264c4752006-01-06 00:18:59 -0800336
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800337 n = snprintf(buf, len, "%s%s/%s", uml_dir, umid, name);
Jeff Dikeba180fd2007-10-16 01:27:00 -0700338 if (n >= len) {
339 printk(UM_KERN_ERR "umid_file_name : buffer too short\n");
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800340 return -E2BIG;
Jeff Dike2264c4752006-01-06 00:18:59 -0800341 }
342
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800343 return 0;
Jeff Dike2264c4752006-01-06 00:18:59 -0800344}
345
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800346char *get_umid(void)
Jeff Dike2264c4752006-01-06 00:18:59 -0800347{
Jeff Dike2264c4752006-01-06 00:18:59 -0800348 return umid;
349}
350
351static int __init set_uml_dir(char *name, int *add)
352{
Jeff Dikeba180fd2007-10-16 01:27:00 -0700353 if (*name == '\0') {
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800354 printf("uml_dir can't be an empty string\n");
355 return 0;
Jeff Dike2264c4752006-01-06 00:18:59 -0800356 }
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800357
Jeff Dikeba180fd2007-10-16 01:27:00 -0700358 if (name[strlen(name) - 1] == '/') {
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800359 uml_dir = name;
360 return 0;
361 }
362
363 uml_dir = malloc(strlen(name) + 2);
Jeff Dikeba180fd2007-10-16 01:27:00 -0700364 if (uml_dir == NULL) {
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800365 printf("Failed to malloc uml_dir - error = %d\n", errno);
366
Jeff Dikeba180fd2007-10-16 01:27:00 -0700367 /*
368 * Return 0 here because do_initcalls doesn't look at
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800369 * the return value.
370 */
371 return 0;
372 }
373 sprintf(uml_dir, "%s/", name);
374
375 return 0;
Jeff Dike2264c4752006-01-06 00:18:59 -0800376}
377
378__uml_setup("uml_dir=", set_uml_dir,
379"uml_dir=<directory>\n"
380" The location to place the pid and umid files.\n\n"
381);
382
383static void remove_umid_dir(void)
384{
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800385 char dir[strlen(uml_dir) + UMID_LEN + 1], err;
Jeff Dike2264c4752006-01-06 00:18:59 -0800386
387 sprintf(dir, "%s%s", uml_dir, umid);
Paolo 'Blaisorblade' Giarrussoeb289312006-07-01 04:36:24 -0700388 err = remove_files_and_dir(dir);
Jeff Dikeba180fd2007-10-16 01:27:00 -0700389 if (err)
Paolo 'Blaisorblade' Giarrussoeb289312006-07-01 04:36:24 -0700390 printf("remove_umid_dir - remove_files_and_dir failed with "
Jeff Dike7eebe8a2006-01-06 00:19:01 -0800391 "err = %d\n", err);
Jeff Dike2264c4752006-01-06 00:18:59 -0800392}
393
394__uml_exitcall(remove_umid_dir);