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
|
/* Copyright 2013 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* Persistence module for emulator */
/* This provides storage that can be opened, closed and reopened by the
* current process at will, whose naming even remains stable across multiple
* invocations of the same executable, while providing a unique name for
* each executable (as determined by path) that uses these routines.
*
* Useful when semi-permanent storage is required even with many
* similar processes running in parallel (e.g. in a highly parallel
* test suite run.
*
* mkstemp and friends don't provide these properties which is why we have
* this homegrown implementation of something similar-yet-different.
*/
#include <linux/limits.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include "builtin/assert.h"
#include "util.h"
/* The longest path in a chroot seems to be about 280 characters (as of
* April 2021) so define a cut-off instead of just hoping for the best:
* If we were to run into a path that is nearly PATH_MAX bytes long,
* file names could end up being reused inadvertedly because the various
* snprintf calls would cut off the trailing characters, so the "tag" (and
* maybe more) is gone even though it only exists for differentiation.
*
* Instead bail out if we encounter a path (to an executable using these
* routines) that is longer than we expect.
*
* Round up for some spare room because why not?
*/
static const int max_len = 300;
/* This must be at least the size of the prefix added in get_storage_path */
static const int max_prefix_len = 25;
static void get_storage_path(char *out)
{
char buf[PATH_MAX];
int sz;
char *current;
sz = readlink("/proc/self/exe", buf, PATH_MAX - 1);
buf[sz] = '\0';
ASSERT(sz <= max_len);
/* replace / by underscores in the path to get the shared memory name */
current = strchr(buf, '/');
while (current) {
*current = '_';
current = strchr(current, '/');
}
sz = snprintf(out, PATH_MAX - 1, "/dev/shm/EC_persist_%.*s", max_len,
buf);
ASSERT(sz > 0);
out[PATH_MAX - 1] = '\0';
ASSERT(sz <= max_len + max_prefix_len);
}
FILE *get_persistent_storage(const char *tag, const char *mode)
{
char buf[PATH_MAX];
char path[PATH_MAX];
int sz;
/* There's no longer tag in use right now, and there shouldn't be. */
ASSERT(strlen(tag) < 32);
/*
* The persistent storage with tag 'foo' for test 'bar' would
* be named 'bar_persist_foo'
*/
get_storage_path(buf);
sz = snprintf(path, PATH_MAX - 1, "%.*s_%32s", max_len + max_prefix_len,
buf, tag);
ASSERT(sz > 0);
path[PATH_MAX - 1] = '\0';
return fopen(path, mode);
}
void release_persistent_storage(FILE *ps)
{
fclose(ps);
}
void remove_persistent_storage(const char *tag)
{
char buf[PATH_MAX];
char path[PATH_MAX];
int sz;
/* There's no longer tag in use right now, and there shouldn't be. */
ASSERT(strlen(tag) < 32);
get_storage_path(buf);
sz = snprintf(path, PATH_MAX - 1, "%.*s_%32s", max_len + max_prefix_len,
buf, tag);
ASSERT(sz > 0);
path[PATH_MAX - 1] = '\0';
unlink(path);
}
|