-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathfscollect.c
More file actions
330 lines (307 loc) · 12.1 KB
/
Copy pathfscollect.c
File metadata and controls
330 lines (307 loc) · 12.1 KB
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
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
/*
* Copyright (c) 2018, Exile Heavy Industries
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted (subject to the limitations in the disclaimer
* below) provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of the copyright holder nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS
* LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#ifndef DFBEADM_FSCOLLECT_H
#include "fscollect.h"
#endif
#ifndef DFBADM_H2TEST_H
#include "fstest.h"
#endif
#ifndef DFBEADM_SNAPFS_H
#include "snapfs.h"
#endif
#define LABELED 0
#define NOBE 1
extern bool dbg;
/*
* create a boot environment
* returns 0 if successful, 1 if error, >=2 if things have gone horribly wrong
*/
int
create(const char *label) {
/* since we can't rely on the VFS layer for all of our fstab data, we need to be sure what exists */
int i, fstabcount, retc, vfscount;
struct fstab *fsptr;
struct statfs *vfsptr;
bedata *befs;
assert(label != NULL);
i = retc = fstabcount = vfscount = 0;
fsptr = NULL;
vfsptr = NULL;
if (dbg) {
fprintf(stderr,"DBG: %s [%s:%u] %s: Entered with label = %s\n",__progname,__FILE__,__LINE__,__func__,label);
}
if ((vfscount = getfsstat(vfsptr, 0, MNT_WAIT)) == 0) {
fprintf(stderr, "ERR: %s [%s:%u] %s: Something's wrong, no filesystems found\n",__progname,__FILE__,__LINE__,__func__);
}
/* Simple loop to get filesystem count from /etc/fstab */
while ((fsptr = getfsent()) != NULL) {
fstabcount++;
}
/* ensure that if we start reading the system fstab again, we start from the top */
endfsent();
/* since there's bound to be a swap partition, decrement fstabcount by one */
if (fstabcount != vfscount) {
fprintf(stderr, "Filesystem counts differ! May have unintended side-effects!\n"
"fstab count: %d\nvfs count: %d\n",fstabcount, vfscount);
} else {
fprintf(stdout, "INF: %s [%s:%u] %s: VFS Layer and FSTAB(5) are in agreement, generating list of boot environment targets...\n",__progname,__FILE__,__LINE__,__func__);
}
/* now that we have an idea what we're working with, let's go about cloning this data */
if ((befs = calloc((size_t)fstabcount, sizeof(bedata))) == NULL) {
fprintf(stderr,"Could not allocate initial buffer!\n");
retc = 2;
}
/* now allocate space for the members */
for (i = 0; i < fstabcount; i++) {
if ((befs[i].fstab.fs_spec = calloc(MNAMELEN, 1)) == NULL) {
fprintf(stderr, "%s [%s:%u] %s: Could not allocate buffer\n",__progname,__FILE__,__LINE__,__func__);
free(befs);
return(2);
}
if ((befs[i].fstab.fs_file = calloc(MNAMELEN, 1)) == NULL) {
fprintf(stderr, "%s [%s:%u] %s: Could not allocate buffer\n",__progname,__FILE__,__LINE__,__func__);
free(befs);
return(2);
}
if ((befs[i].fstab.fs_vfstype = calloc(MNAMELEN, 1)) == NULL) {
fprintf(stderr, "%s [%s:%u] %s: Could not allocate buffer\n",__progname,__FILE__,__LINE__,__func__);
free(befs);
return(2);
}
if ((befs[i].fstab.fs_mntops = calloc(MNAMELEN, 1)) == NULL) {
fprintf(stderr, "%s [%s:%u] %s: Could not allocate buffer\n",__progname,__FILE__,__LINE__,__func__);
free(befs);
return(2);
}
if ((befs[i].fstab.fs_type = calloc(MNAMELEN, 1)) == NULL) {
fprintf(stderr, "%s [%s:%u] %s: Could not allocate buffer\n",__progname,__FILE__,__LINE__,__func__);
free(befs);
return(2);
}
}
/* now pass the buffers to the next step */
mktargets(befs, fstabcount, label);
/* ensure we clean up after ourselves */
for (i ^= i; i < fstabcount; i++) {
free(befs[i].fstab.fs_spec);
free(befs[i].fstab.fs_file);
free(befs[i].fstab.fs_vfstype);
free(befs[i].fstab.fs_mntops);
free(befs[i].fstab.fs_type);
}
free(befs);
if (dbg) {
fprintf(stderr,"DBG: %s [%s:%u] %s: Returning %d to caller\n",__progname,__FILE__,__LINE__,__func__,retc);
}
return(retc);
}
/*
* TODO: Have this function call openfs() prior to exit.
* It'll cause a slightly deeper call stack than I'd like,
* but fits better logically.
* Creates a buffer of targets to be handed off to snapfs()
* This function should be called directly from create(), and provided
* with a buffer of currently existing filesystems
*/
void
mktargets(bedata *target, int fscount, const char *label) {
register int i, ret;
struct fstab *current;
assert((target != NULL) && (label != NULL));
ret = 0;
if (dbg) {
fprintf(stderr,"DBG: %s [%s:%u] %s: Entered with target = %p, fscount = %d, label = %s\n",
__progname,__FILE__,__LINE__,__func__,(void *)target,fscount,label);
}
/*
* now that we have the number of existing filesystems and the
* bedata struct to fill in, we can go about updating the information
* this is still prior to actually generating the ephemeral fstab though
* we're just building the struct.
*/
for (i = 0; i < fscount; i++) {
current = getfsent();
/* these steps are done for every filesystem */
strlcpy(target[i].fstab.fs_spec, current->fs_spec, MNAMELEN);
strlcpy(target[i].fstab.fs_file, current->fs_file, MNAMELEN);
strlcpy(target[i].fstab.fs_vfstype, current->fs_vfstype, MNAMELEN);
strlcpy(target[i].fstab.fs_mntops, current->fs_mntops, MNAMELEN);
strlcpy(target[i].fstab.fs_type, current->fs_type, MNAMELEN);
target[i].fstab.fs_freq = current->fs_freq;
target[i].fstab.fs_passno = current->fs_passno;
/* now do some additional work in the same loop */
if (ish2(current->fs_file)) {
target[i].snap = true;
openfs(target[i].fstab.fs_file,&target[i].mountfd);
if ((ret = relabel(&target[i], label)) != LABELED) {
if ((ret = newlabel(&target[i], label)) != LABELED) {
/* Assume failure, remove from snapshot candidacy */
fprintf(stderr,"ERR: %s [%s:%u] %s: Unable to write label %s to %s!\n",__progname,__FILE__,__LINE__,__func__,label,target[i].fstab.fs_file);
target[i].snap = false;
close(target[i].mountfd); /* XXX: may not be necessary, but should not cause issues */
}
}
} else {
target[i].snap = false;
}
}
/*
* now everything should be in place to create snapshots
* looping is handled internally
*/
snapfs(target, fscount, label);
if (dbg) {
fprintf(stderr,"DBG: %s [%s:%u] %s: Returning to caller\n",__progname,__FILE__,__LINE__,__func__);
}
}
/*
* return 0 if relabeling is done successfully
* nonzero indicates error, no meaning assigned to
* return values yet
*/
int
relabel(bedata *fs, const char *label) {
char *found, fsbuf[NAME_MAX];
int i, retc;
i = retc = 0;
found = NULL;
assert((fs != NULL) && (label != NULL));
if (dbg) {
fprintf(stderr,"DBG: %s [%s:%u] %s: Entering with fs = %p, label = %s\n",__progname,__FILE__,__LINE__,__func__,(void *)fs,label);
}
/*
* XXX: this function, along with ish2(), will almost certainly need significant rewriting
* as the current mean sof detecting a NULLFS mount vs a HAMMER2 mount are
* ill-defined at this point. It's definitely possible to fail silently, but that shouldn't be
* necessary.
*/
/* simply check for the existence of a boot environment */
if ((found = strchr(fs->fstab.fs_spec, BESEP)) == NULL) {
/* This means there's no indication of a dfbeadm compliant snapshot here, so we can just jump into the snapshot creation logic */
fprintf(stderr,"INF: %s [%s:%u] %s: No existing boot environment found for %s\n",
__progname,__FILE__,__LINE__,__func__,fs->fstab.fs_spec);
retc = NOBE;
} else {
for (i ^= i; *found != 0 && i < NAME_MAX; found++) {
fs->curlabel[i] = *found; /* copy the found label one character at a time into fs->curlabel */
i++;
}
if (dbg) {
fprintf(stderr,"DBG: %s [%s:%u] %s: %d iterations to copy fs->curlabel=(%s)\n",__progname,__FILE__,__LINE__,__func__,i,fs->curlabel);
}
/* see if the label is too long to fit in the allocated space */
if ((NAME_MAX - 1)< ((unsigned int)i + strlen(label))) {
fprintf(stderr,"ERR: %s [%s:%u] %s: Given name of %s is too long!\n", __progname,__FILE__,__LINE__,__func__,label);
} else {
found -= i;
clearBElabel(found);
/* write the new file spec into pfs snapshot struct */
snprintf(fsbuf, (NAME_MAX -1), "%s%c%s",fs->fstab.fs_spec,BESEP,label); /* no longer necessary, but nice for visualizations */
/* XXX: This may actually be copying too much data into the structure, test with label only */
snprintf(fs->snapshot.name, (NAME_MAX - 1), "%s%c%s", (strchr(fs->fstab.fs_spec,PFSDELIM) + 1), BESEP, label);
if (dbg) {
fprintf(stderr,"DBG: %s [%s:%u] %s: Generated new label of (fsbuf)=%s from (fs->fstab.fs_spec)=%s%s\n",__progname,__FILE__,__LINE__,__func__,fsbuf,fs->fstab.fs_spec,fs->curlabel);
}
/* Seems like a good idea, but then snapfs.c:xtractLabel() would need to be updated to handle this properly */
//memset(fs->fstab.fs_spec,0,(size_t)NAME_MAX); /* clear out the current fstab block device entry prior to being passed to snapfs */
}
fs->curlabel[i] = 0; /* ensure NULL termination */
}
if (dbg) {
fprintf(stderr,"DBG: %s [%s:%u] %s: Returning %d to caller\n", __progname,__FILE__,__LINE__,__func__,retc);
}
return(retc);
}
/*
* XXX: Ensure that this is properly migrated from snapfs.c
*/
int
openfs(const char *mountpoint, int *fsfd) {
int retc;
retc = 0;
/* Ensure we can't try to open mountpoints without escalated privileges */
assert((geteuid() == 0) && (mountpoint != NULL));
if (dbg) {
fprintf(stderr,"DBG: %s [%s:%u] %s: Entering with mountpoint = %s\n",__progname,__FILE__,__LINE__,__func__,mountpoint);
}
if ((retc = open(mountpoint,O_RDONLY)) > 0) {
*fsfd = retc;
retc ^= retc;
} else {
fprintf(stderr,"ERR: %s [%s:%u] %s: Error opening %s: %s\n",__progname,__FILE__,__LINE__,__func__,mountpoint,strerror(errno));
}
if (dbg) {
fprintf(stderr,"DBG: %s [%s:%u] %s: Opened fd %d for %s, returning %d to caller\n",__progname,__FILE__,__LINE__,__func__,*fsfd,mountpoint,retc);
}
return(retc);
}
/*
* XXX: Maybe not needed, 0's out the name of
* an existing boot environment label
*/
int
clearBElabel(char *label) {
int retc;
retc = 0;
assert(label != NULL);
for (;*label != 0; label++) {
*label ^= *label;
}
return(retc);
}
int
newlabel(bedata *fs, const char *label) {
int retc;
retc = LABELED; /* same as 0, assume success */
assert((fs != NULL) && (label != NULL));
if (dbg) {
fprintf(stderr,"DBG: %s [%s:%u] %s: Entering with fs = %p, label = %s\n", __progname,__FILE__,__LINE__,__func__,(void *)fs, label);
}
/* Alert user that name might be too long */
if (strlen(label) + strlen(fs->fstab.fs_spec) > (NAME_MAX - 1)) {
fprintf(stderr,"WRN: %s [%s:%u] %s: Given label (%s) is too long and will be truncated!\n",__progname,__FILE__,__LINE__,__func__,label);
}
snprintf(fs->snapshot.name,(NAME_MAX - 1), "%s%c%s", (strchr(fs->fstab.fs_spec,PFSDELIM) + 1), BESEP, label);
if (dbg) {
fprintf(stderr,"DBG: %s [%s:%u] %s: Returning %d to caller\n",__progname,__FILE__,__LINE__,__func__,retc);
}
return(retc);
}