xref: /freebsd/contrib/openbsm/bin/auditdistd/trail.c (revision aa77200569e397d6ff1fdb4d255d0fa254d0a128)
1*aa772005SRobert Watson /*-
2*aa772005SRobert Watson  * Copyright (c) 2012 The FreeBSD Foundation
3*aa772005SRobert Watson  * All rights reserved.
4*aa772005SRobert Watson  *
5*aa772005SRobert Watson  * This software was developed by Pawel Jakub Dawidek under sponsorship from
6*aa772005SRobert Watson  * the FreeBSD Foundation.
7*aa772005SRobert Watson  *
8*aa772005SRobert Watson  * Redistribution and use in source and binary forms, with or without
9*aa772005SRobert Watson  * modification, are permitted provided that the following conditions
10*aa772005SRobert Watson  * are met:
11*aa772005SRobert Watson  * 1. Redistributions of source code must retain the above copyright
12*aa772005SRobert Watson  *    notice, this list of conditions and the following disclaimer.
13*aa772005SRobert Watson  * 2. Redistributions in binary form must reproduce the above copyright
14*aa772005SRobert Watson  *    notice, this list of conditions and the following disclaimer in the
15*aa772005SRobert Watson  *    documentation and/or other materials provided with the distribution.
16*aa772005SRobert Watson  *
17*aa772005SRobert Watson  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18*aa772005SRobert Watson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19*aa772005SRobert Watson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20*aa772005SRobert Watson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21*aa772005SRobert Watson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22*aa772005SRobert Watson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23*aa772005SRobert Watson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24*aa772005SRobert Watson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25*aa772005SRobert Watson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26*aa772005SRobert Watson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27*aa772005SRobert Watson  * SUCH DAMAGE.
28*aa772005SRobert Watson  *
29*aa772005SRobert Watson  * $P4: //depot/projects/trustedbsd/openbsm/bin/auditdistd/trail.c#3 $
30*aa772005SRobert Watson  */
31*aa772005SRobert Watson 
32*aa772005SRobert Watson #include <config/config.h>
33*aa772005SRobert Watson 
34*aa772005SRobert Watson #include <sys/param.h>
35*aa772005SRobert Watson #include <sys/stat.h>
36*aa772005SRobert Watson 
37*aa772005SRobert Watson #include <dirent.h>
38*aa772005SRobert Watson #include <errno.h>
39*aa772005SRobert Watson #include <fcntl.h>
40*aa772005SRobert Watson #include <stdbool.h>
41*aa772005SRobert Watson #include <stdint.h>
42*aa772005SRobert Watson #include <stdlib.h>
43*aa772005SRobert Watson #include <string.h>
44*aa772005SRobert Watson #include <unistd.h>
45*aa772005SRobert Watson 
46*aa772005SRobert Watson #include <compat/compat.h>
47*aa772005SRobert Watson #ifndef HAVE_STRLCPY
48*aa772005SRobert Watson #include <compat/strlcpy.h>
49*aa772005SRobert Watson #endif
50*aa772005SRobert Watson #ifndef HAVE_FACCESSAT
51*aa772005SRobert Watson #include "faccessat.h"
52*aa772005SRobert Watson #endif
53*aa772005SRobert Watson #ifndef HAVE_FSTATAT
54*aa772005SRobert Watson #include "fstatat.h"
55*aa772005SRobert Watson #endif
56*aa772005SRobert Watson #ifndef HAVE_OPENAT
57*aa772005SRobert Watson #include "openat.h"
58*aa772005SRobert Watson #endif
59*aa772005SRobert Watson #ifndef HAVE_UNLINKAT
60*aa772005SRobert Watson #include "unlinkat.h"
61*aa772005SRobert Watson #endif
62*aa772005SRobert Watson 
63*aa772005SRobert Watson #include "pjdlog.h"
64*aa772005SRobert Watson #include "trail.h"
65*aa772005SRobert Watson 
66*aa772005SRobert Watson #define	TRAIL_MAGIC	0x79a11
67*aa772005SRobert Watson struct trail {
68*aa772005SRobert Watson 	int	 tr_magic;
69*aa772005SRobert Watson 	/* Path usually to /var/audit/dist/ directory. */
70*aa772005SRobert Watson 	char	 tr_dirname[PATH_MAX];
71*aa772005SRobert Watson 	/* Descriptor to td_dirname directory. */
72*aa772005SRobert Watson 	DIR	*tr_dirfp;
73*aa772005SRobert Watson 	/* Path to audit trail file. */
74*aa772005SRobert Watson 	char	 tr_filename[PATH_MAX];
75*aa772005SRobert Watson 	/* Descriptor to audit trail file. */
76*aa772005SRobert Watson 	int	 tr_filefd;
77*aa772005SRobert Watson };
78*aa772005SRobert Watson 
79*aa772005SRobert Watson #define	HALF_LEN	14
80*aa772005SRobert Watson 
81*aa772005SRobert Watson bool
82*aa772005SRobert Watson trail_is_not_terminated(const char *filename)
83*aa772005SRobert Watson {
84*aa772005SRobert Watson 
85*aa772005SRobert Watson 	return (strcmp(filename + HALF_LEN, ".not_terminated") == 0);
86*aa772005SRobert Watson }
87*aa772005SRobert Watson 
88*aa772005SRobert Watson bool
89*aa772005SRobert Watson trail_is_crash_recovery(const char *filename)
90*aa772005SRobert Watson {
91*aa772005SRobert Watson 
92*aa772005SRobert Watson 	return (strcmp(filename + HALF_LEN, ".crash_recovery") == 0);
93*aa772005SRobert Watson }
94*aa772005SRobert Watson 
95*aa772005SRobert Watson struct trail *
96*aa772005SRobert Watson trail_new(const char *dirname, bool create)
97*aa772005SRobert Watson {
98*aa772005SRobert Watson 	struct trail *trail;
99*aa772005SRobert Watson 
100*aa772005SRobert Watson 	trail = calloc(1, sizeof(*trail));
101*aa772005SRobert Watson 
102*aa772005SRobert Watson 	if (strlcpy(trail->tr_dirname, dirname, sizeof(trail->tr_dirname)) >=
103*aa772005SRobert Watson 	    sizeof(trail->tr_dirname)) {
104*aa772005SRobert Watson 		free(trail);
105*aa772005SRobert Watson 		pjdlog_error("Directory name too long (\"%s\").", dirname);
106*aa772005SRobert Watson 		errno = ENAMETOOLONG;
107*aa772005SRobert Watson 		return (NULL);
108*aa772005SRobert Watson 	}
109*aa772005SRobert Watson 	trail->tr_dirfp = opendir(dirname);
110*aa772005SRobert Watson 	if (trail->tr_dirfp == NULL) {
111*aa772005SRobert Watson 		if (create && errno == ENOENT) {
112*aa772005SRobert Watson 			if (mkdir(dirname, 0700) == -1) {
113*aa772005SRobert Watson 				pjdlog_errno(LOG_ERR,
114*aa772005SRobert Watson 				    "Unable to create directory \"%s\"",
115*aa772005SRobert Watson 				    dirname);
116*aa772005SRobert Watson 				free(trail);
117*aa772005SRobert Watson 				return (NULL);
118*aa772005SRobert Watson 			}
119*aa772005SRobert Watson 			/* TODO: Set directory ownership. */
120*aa772005SRobert Watson 		} else {
121*aa772005SRobert Watson 			pjdlog_errno(LOG_ERR,
122*aa772005SRobert Watson 			    "Unable to open directory \"%s\"",
123*aa772005SRobert Watson 			    dirname);
124*aa772005SRobert Watson 			free(trail);
125*aa772005SRobert Watson 			return (NULL);
126*aa772005SRobert Watson 		}
127*aa772005SRobert Watson 		trail->tr_dirfp = opendir(dirname);
128*aa772005SRobert Watson 		if (trail->tr_dirfp == NULL) {
129*aa772005SRobert Watson 			pjdlog_errno(LOG_ERR,
130*aa772005SRobert Watson 			    "Unable to open directory \"%s\"",
131*aa772005SRobert Watson 			    dirname);
132*aa772005SRobert Watson 			free(trail);
133*aa772005SRobert Watson 			return (NULL);
134*aa772005SRobert Watson 		}
135*aa772005SRobert Watson 	}
136*aa772005SRobert Watson 	trail->tr_filefd = -1;
137*aa772005SRobert Watson 	trail->tr_magic = TRAIL_MAGIC;
138*aa772005SRobert Watson 	return (trail);
139*aa772005SRobert Watson }
140*aa772005SRobert Watson 
141*aa772005SRobert Watson void
142*aa772005SRobert Watson trail_free(struct trail *trail)
143*aa772005SRobert Watson {
144*aa772005SRobert Watson 
145*aa772005SRobert Watson 	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
146*aa772005SRobert Watson 
147*aa772005SRobert Watson 	if (trail->tr_filefd != -1)
148*aa772005SRobert Watson 		trail_close(trail);
149*aa772005SRobert Watson 	closedir(trail->tr_dirfp);
150*aa772005SRobert Watson 	bzero(trail, sizeof(*trail));
151*aa772005SRobert Watson 	trail->tr_magic = 0;
152*aa772005SRobert Watson 	trail->tr_filefd = -1;
153*aa772005SRobert Watson 	free(trail);
154*aa772005SRobert Watson }
155*aa772005SRobert Watson 
156*aa772005SRobert Watson static uint8_t
157*aa772005SRobert Watson trail_type(DIR *dirfp, const char *filename)
158*aa772005SRobert Watson {
159*aa772005SRobert Watson 	struct stat sb;
160*aa772005SRobert Watson 	int dfd;
161*aa772005SRobert Watson 
162*aa772005SRobert Watson 	PJDLOG_ASSERT(dirfp != NULL);
163*aa772005SRobert Watson 
164*aa772005SRobert Watson 	dfd = dirfd(dirfp);
165*aa772005SRobert Watson 	PJDLOG_ASSERT(dfd >= 0);
166*aa772005SRobert Watson 	if (fstatat(dfd, filename, &sb, AT_SYMLINK_NOFOLLOW) == -1) {
167*aa772005SRobert Watson 		pjdlog_errno(LOG_ERR, "Unable to stat \"%s\"", filename);
168*aa772005SRobert Watson 		return (DT_UNKNOWN);
169*aa772005SRobert Watson 	}
170*aa772005SRobert Watson 	return (IFTODT(sb.st_mode));
171*aa772005SRobert Watson }
172*aa772005SRobert Watson 
173*aa772005SRobert Watson /*
174*aa772005SRobert Watson  * Find trail file by first part of the name in case it was renamed.
175*aa772005SRobert Watson  * First part of the trail file name never changes, but trail file
176*aa772005SRobert Watson  * can be renamed when hosts are disconnected from .not_terminated
177*aa772005SRobert Watson  * to .[0-9]{14} or to .crash_recovery.
178*aa772005SRobert Watson  */
179*aa772005SRobert Watson static bool
180*aa772005SRobert Watson trail_find(struct trail *trail)
181*aa772005SRobert Watson {
182*aa772005SRobert Watson 	struct dirent *dp;
183*aa772005SRobert Watson 
184*aa772005SRobert Watson 	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
185*aa772005SRobert Watson 	PJDLOG_ASSERT(trail_is_not_terminated(trail->tr_filename));
186*aa772005SRobert Watson 
187*aa772005SRobert Watson 	rewinddir(trail->tr_dirfp);
188*aa772005SRobert Watson 	while ((dp = readdir(trail->tr_dirfp)) != NULL) {
189*aa772005SRobert Watson 		if (strncmp(dp->d_name, trail->tr_filename, HALF_LEN + 1) == 0)
190*aa772005SRobert Watson 			break;
191*aa772005SRobert Watson 	}
192*aa772005SRobert Watson 	if (dp == NULL)
193*aa772005SRobert Watson 		return (false);
194*aa772005SRobert Watson 	PJDLOG_VERIFY(strlcpy(trail->tr_filename, dp->d_name,
195*aa772005SRobert Watson 	    sizeof(trail->tr_filename)) < sizeof(trail->tr_filename));
196*aa772005SRobert Watson 	return (true);
197*aa772005SRobert Watson }
198*aa772005SRobert Watson 
199*aa772005SRobert Watson /*
200*aa772005SRobert Watson  * Open the given trail file and move pointer at the given offset, as this is
201*aa772005SRobert Watson  * where receiver finished the last time.
202*aa772005SRobert Watson  * If the file doesn't exist or the given offset is equal to the file size,
203*aa772005SRobert Watson  * move to the next trail file.
204*aa772005SRobert Watson  */
205*aa772005SRobert Watson void
206*aa772005SRobert Watson trail_start(struct trail *trail, const char *filename, off_t offset)
207*aa772005SRobert Watson {
208*aa772005SRobert Watson 	struct stat sb;
209*aa772005SRobert Watson 	int dfd, fd;
210*aa772005SRobert Watson 
211*aa772005SRobert Watson 	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
212*aa772005SRobert Watson 
213*aa772005SRobert Watson 	PJDLOG_VERIFY(strlcpy(trail->tr_filename, filename,
214*aa772005SRobert Watson 	    sizeof(trail->tr_filename)) < sizeof(trail->tr_filename));
215*aa772005SRobert Watson 	trail->tr_filefd = -1;
216*aa772005SRobert Watson 
217*aa772005SRobert Watson 	if (trail->tr_filename[0] == '\0') {
218*aa772005SRobert Watson 		PJDLOG_ASSERT(offset == 0);
219*aa772005SRobert Watson 		trail_next(trail);
220*aa772005SRobert Watson 		return;
221*aa772005SRobert Watson 	}
222*aa772005SRobert Watson 
223*aa772005SRobert Watson 	dfd = dirfd(trail->tr_dirfp);
224*aa772005SRobert Watson 	PJDLOG_ASSERT(dfd >= 0);
225*aa772005SRobert Watson again:
226*aa772005SRobert Watson 	fd = openat(dfd, trail->tr_filename, O_RDONLY);
227*aa772005SRobert Watson 	if (fd == -1) {
228*aa772005SRobert Watson 		if (errno == ENOENT &&
229*aa772005SRobert Watson 		    trail_is_not_terminated(trail->tr_filename) &&
230*aa772005SRobert Watson 		    trail_find(trail)) {
231*aa772005SRobert Watson 			/* File was renamed. Retry with new name. */
232*aa772005SRobert Watson 			pjdlog_debug(1,
233*aa772005SRobert Watson 			   "Trail file was renamed since last connection to \"%s/%s\".",
234*aa772005SRobert Watson 			   trail->tr_dirname, trail->tr_filename);
235*aa772005SRobert Watson 			goto again;
236*aa772005SRobert Watson 		} else if (errno == ENOENT) {
237*aa772005SRobert Watson 			/* File disappeared. */
238*aa772005SRobert Watson 			pjdlog_debug(1, "File \"%s/%s\" doesn't exist.",
239*aa772005SRobert Watson 			    trail->tr_dirname, trail->tr_filename);
240*aa772005SRobert Watson 		} else {
241*aa772005SRobert Watson 			pjdlog_errno(LOG_ERR,
242*aa772005SRobert Watson 			    "Unable to open file \"%s/%s\", skipping",
243*aa772005SRobert Watson 			    trail->tr_dirname, trail->tr_filename);
244*aa772005SRobert Watson 		}
245*aa772005SRobert Watson 		trail_next(trail);
246*aa772005SRobert Watson 		return;
247*aa772005SRobert Watson 	}
248*aa772005SRobert Watson 	if (fstat(fd, &sb) == -1) {
249*aa772005SRobert Watson 		pjdlog_errno(LOG_ERR,
250*aa772005SRobert Watson 		    "Unable to stat file \"%s/%s\", skipping",
251*aa772005SRobert Watson 		    trail->tr_dirname, trail->tr_filename);
252*aa772005SRobert Watson 		close(fd);
253*aa772005SRobert Watson 		trail_next(trail);
254*aa772005SRobert Watson 		return;
255*aa772005SRobert Watson 	}
256*aa772005SRobert Watson 	if (!S_ISREG(sb.st_mode)) {
257*aa772005SRobert Watson 		pjdlog_warning("File \"%s/%s\" is not a regular file, skipping.",
258*aa772005SRobert Watson 		    trail->tr_dirname, trail->tr_filename);
259*aa772005SRobert Watson 		close(fd);
260*aa772005SRobert Watson 		trail_next(trail);
261*aa772005SRobert Watson 		return;
262*aa772005SRobert Watson 	}
263*aa772005SRobert Watson 	/*
264*aa772005SRobert Watson 	 * We continue sending requested file if:
265*aa772005SRobert Watson 	 * 1. It is not fully sent yet, or
266*aa772005SRobert Watson 	 * 2. It is fully sent, but is not terminated, so new data can be
267*aa772005SRobert Watson 	 *    appended still, or
268*aa772005SRobert Watson 	 * 3. It is fully sent but file name has changed.
269*aa772005SRobert Watson 	 *
270*aa772005SRobert Watson 	 * Note that we are fine if our .not_terminated or .crash_recovery file
271*aa772005SRobert Watson 	 * is smaller than the one on the receiver side, as it is possible that
272*aa772005SRobert Watson 	 * more data was send to the receiver than was safely stored on disk.
273*aa772005SRobert Watson 	 * We accept .not_terminated only because auditdistd can start before
274*aa772005SRobert Watson 	 * auditd manage to rename it to .crash_recovery.
275*aa772005SRobert Watson 	 */
276*aa772005SRobert Watson 	if (offset < sb.st_size ||
277*aa772005SRobert Watson 	    (offset >= sb.st_size &&
278*aa772005SRobert Watson 	     trail_is_not_terminated(trail->tr_filename)) ||
279*aa772005SRobert Watson 	    (offset >= sb.st_size && trail_is_not_terminated(filename) &&
280*aa772005SRobert Watson 	     trail_is_crash_recovery(trail->tr_filename))) {
281*aa772005SRobert Watson 		/* File was not fully send. Let's finish it. */
282*aa772005SRobert Watson 		if (lseek(fd, offset, SEEK_SET) == -1) {
283*aa772005SRobert Watson 			pjdlog_errno(LOG_ERR,
284*aa772005SRobert Watson 			    "Unable to move to offset %jd within file \"%s/%s\", skipping",
285*aa772005SRobert Watson 			    (intmax_t)offset, trail->tr_dirname,
286*aa772005SRobert Watson 			    trail->tr_filename);
287*aa772005SRobert Watson 			close(fd);
288*aa772005SRobert Watson 			trail_next(trail);
289*aa772005SRobert Watson 			return;
290*aa772005SRobert Watson 		}
291*aa772005SRobert Watson 		if (!trail_is_crash_recovery(trail->tr_filename)) {
292*aa772005SRobert Watson 			pjdlog_debug(1,
293*aa772005SRobert Watson 			    "Restarting file \"%s/%s\" at offset %jd.",
294*aa772005SRobert Watson 			    trail->tr_dirname, trail->tr_filename,
295*aa772005SRobert Watson 			    (intmax_t)offset);
296*aa772005SRobert Watson 		}
297*aa772005SRobert Watson 		trail->tr_filefd = fd;
298*aa772005SRobert Watson 		return;
299*aa772005SRobert Watson 	}
300*aa772005SRobert Watson 	close(fd);
301*aa772005SRobert Watson 	if (offset > sb.st_size) {
302*aa772005SRobert Watson 		pjdlog_warning("File \"%s/%s\" shrinked, removing it.",
303*aa772005SRobert Watson 		    trail->tr_dirname, trail->tr_filename);
304*aa772005SRobert Watson 	} else {
305*aa772005SRobert Watson 		pjdlog_debug(1, "File \"%s/%s\" is already sent, removing it.",
306*aa772005SRobert Watson 		    trail->tr_dirname, trail->tr_filename);
307*aa772005SRobert Watson 	}
308*aa772005SRobert Watson 	/* Entire file is already sent or it shirnked, we can remove it. */
309*aa772005SRobert Watson 	if (unlinkat(dfd, trail->tr_filename, 0) == -1) {
310*aa772005SRobert Watson 		pjdlog_errno(LOG_WARNING, "Unable to remove file \"%s/%s\"",
311*aa772005SRobert Watson 		    trail->tr_dirname, trail->tr_filename);
312*aa772005SRobert Watson 	}
313*aa772005SRobert Watson 	trail_next(trail);
314*aa772005SRobert Watson }
315*aa772005SRobert Watson 
316*aa772005SRobert Watson /*
317*aa772005SRobert Watson  * Set next file in the trail->tr_dirname directory and open it for reading.
318*aa772005SRobert Watson  */
319*aa772005SRobert Watson void
320*aa772005SRobert Watson trail_next(struct trail *trail)
321*aa772005SRobert Watson {
322*aa772005SRobert Watson 	char curfile[PATH_MAX];
323*aa772005SRobert Watson 	struct dirent *dp;
324*aa772005SRobert Watson 	int dfd;
325*aa772005SRobert Watson 
326*aa772005SRobert Watson 	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
327*aa772005SRobert Watson 	PJDLOG_ASSERT(trail->tr_filefd == -1);
328*aa772005SRobert Watson 
329*aa772005SRobert Watson again:
330*aa772005SRobert Watson 	curfile[0] = '\0';
331*aa772005SRobert Watson 
332*aa772005SRobert Watson 	rewinddir(trail->tr_dirfp);
333*aa772005SRobert Watson 	while ((dp = readdir(trail->tr_dirfp)) != NULL) {
334*aa772005SRobert Watson 		if (dp->d_name[0] < '0' || dp->d_name[0] > '9')
335*aa772005SRobert Watson 			continue;
336*aa772005SRobert Watson 		if (dp->d_type == DT_UNKNOWN)
337*aa772005SRobert Watson 			dp->d_type = trail_type(trail->tr_dirfp, dp->d_name);
338*aa772005SRobert Watson 		/* We are only interested in regular files, skip the rest. */
339*aa772005SRobert Watson 		if (dp->d_type != DT_REG) {
340*aa772005SRobert Watson 			pjdlog_debug(1,
341*aa772005SRobert Watson 			    "File \"%s/%s\" is not a regular file, skipping.",
342*aa772005SRobert Watson 			    trail->tr_dirname, dp->d_name);
343*aa772005SRobert Watson 			continue;
344*aa772005SRobert Watson 		}
345*aa772005SRobert Watson 		/* Skip all files "greater" than curfile. */
346*aa772005SRobert Watson 		if (curfile[0] != '\0' && strcmp(dp->d_name, curfile) > 0)
347*aa772005SRobert Watson 			continue;
348*aa772005SRobert Watson 		/* Skip all files "smaller" than the current trail_filename. */
349*aa772005SRobert Watson 		if (trail->tr_filename[0] != '\0' &&
350*aa772005SRobert Watson 		    strcmp(dp->d_name, trail->tr_filename) <= 0) {
351*aa772005SRobert Watson 			continue;
352*aa772005SRobert Watson 		}
353*aa772005SRobert Watson 		PJDLOG_VERIFY(strlcpy(curfile, dp->d_name, sizeof(curfile)) <
354*aa772005SRobert Watson 		    sizeof(curfile));
355*aa772005SRobert Watson 	}
356*aa772005SRobert Watson 	if (curfile[0] == '\0') {
357*aa772005SRobert Watson 		/*
358*aa772005SRobert Watson 		 * There are no new trail files, so we return.
359*aa772005SRobert Watson 		 * We don't clear trail_filename string, to know where to
360*aa772005SRobert Watson 		 * start when new file appears.
361*aa772005SRobert Watson 		 */
362*aa772005SRobert Watson 		PJDLOG_ASSERT(trail->tr_filefd == -1);
363*aa772005SRobert Watson 		pjdlog_debug(1, "No new trail files.");
364*aa772005SRobert Watson 		return;
365*aa772005SRobert Watson 	}
366*aa772005SRobert Watson 	PJDLOG_VERIFY(strlcpy(trail->tr_filename, curfile,
367*aa772005SRobert Watson 	    sizeof(trail->tr_filename)) < sizeof(trail->tr_filename));
368*aa772005SRobert Watson 	dfd = dirfd(trail->tr_dirfp);
369*aa772005SRobert Watson 	PJDLOG_ASSERT(dfd >= 0);
370*aa772005SRobert Watson 	trail->tr_filefd = openat(dfd, trail->tr_filename, O_RDONLY);
371*aa772005SRobert Watson 	if (trail->tr_filefd == -1) {
372*aa772005SRobert Watson 		pjdlog_errno(LOG_ERR,
373*aa772005SRobert Watson 		    "Unable to open file \"%s/%s\", skipping",
374*aa772005SRobert Watson 		    trail->tr_dirname, trail->tr_filename);
375*aa772005SRobert Watson 		goto again;
376*aa772005SRobert Watson 	}
377*aa772005SRobert Watson 	pjdlog_debug(1, "Found next trail file: \"%s/%s\".", trail->tr_dirname,
378*aa772005SRobert Watson 	    trail->tr_filename);
379*aa772005SRobert Watson }
380*aa772005SRobert Watson 
381*aa772005SRobert Watson /*
382*aa772005SRobert Watson  * Close current trial file.
383*aa772005SRobert Watson  */
384*aa772005SRobert Watson void
385*aa772005SRobert Watson trail_close(struct trail *trail)
386*aa772005SRobert Watson {
387*aa772005SRobert Watson 
388*aa772005SRobert Watson 	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
389*aa772005SRobert Watson 	PJDLOG_ASSERT(trail->tr_filefd >= 0);
390*aa772005SRobert Watson 	PJDLOG_ASSERT(trail->tr_filename[0] != '\0');
391*aa772005SRobert Watson 
392*aa772005SRobert Watson 	PJDLOG_VERIFY(close(trail->tr_filefd) == 0);
393*aa772005SRobert Watson 	trail->tr_filefd = -1;
394*aa772005SRobert Watson }
395*aa772005SRobert Watson 
396*aa772005SRobert Watson /*
397*aa772005SRobert Watson  * Reset trail state. Used when connection is disconnected and we will
398*aa772005SRobert Watson  * need to start over after reconnect. Trail needs to be already closed.
399*aa772005SRobert Watson  */
400*aa772005SRobert Watson void
401*aa772005SRobert Watson trail_reset(struct trail *trail)
402*aa772005SRobert Watson {
403*aa772005SRobert Watson 
404*aa772005SRobert Watson 	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
405*aa772005SRobert Watson 	PJDLOG_ASSERT(trail->tr_filefd == -1);
406*aa772005SRobert Watson 
407*aa772005SRobert Watson 	trail->tr_filename[0] = '\0';
408*aa772005SRobert Watson }
409*aa772005SRobert Watson 
410*aa772005SRobert Watson /*
411*aa772005SRobert Watson  * Unlink current trial file.
412*aa772005SRobert Watson  */
413*aa772005SRobert Watson void
414*aa772005SRobert Watson trail_unlink(struct trail *trail, const char *filename)
415*aa772005SRobert Watson {
416*aa772005SRobert Watson 	int dfd;
417*aa772005SRobert Watson 
418*aa772005SRobert Watson 	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
419*aa772005SRobert Watson 	PJDLOG_ASSERT(filename != NULL);
420*aa772005SRobert Watson 	PJDLOG_ASSERT(filename[0] != '\0');
421*aa772005SRobert Watson 
422*aa772005SRobert Watson 	dfd = dirfd(trail->tr_dirfp);
423*aa772005SRobert Watson 	PJDLOG_ASSERT(dfd >= 0);
424*aa772005SRobert Watson 
425*aa772005SRobert Watson 	if (unlinkat(dfd, filename, 0) == -1) {
426*aa772005SRobert Watson 		pjdlog_errno(LOG_ERR, "Unable to remove \"%s/%s\"",
427*aa772005SRobert Watson 		    trail->tr_dirname, filename);
428*aa772005SRobert Watson 	} else {
429*aa772005SRobert Watson 		pjdlog_debug(1, "Trail file \"%s/%s\" removed.",
430*aa772005SRobert Watson 		    trail->tr_dirname, filename);
431*aa772005SRobert Watson 	}
432*aa772005SRobert Watson }
433*aa772005SRobert Watson 
434*aa772005SRobert Watson /*
435*aa772005SRobert Watson  * Return true if we should switch to next trail file.
436*aa772005SRobert Watson  * We don't switch if our file name ends with ".not_terminated" and it
437*aa772005SRobert Watson  * exists (ie. wasn't renamed).
438*aa772005SRobert Watson  */
439*aa772005SRobert Watson bool
440*aa772005SRobert Watson trail_switch(struct trail *trail)
441*aa772005SRobert Watson {
442*aa772005SRobert Watson 	char filename[PATH_MAX];
443*aa772005SRobert Watson 	int fd;
444*aa772005SRobert Watson 
445*aa772005SRobert Watson 	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
446*aa772005SRobert Watson 	PJDLOG_ASSERT(trail->tr_filefd >= 0);
447*aa772005SRobert Watson 
448*aa772005SRobert Watson 	if (!trail_is_not_terminated(trail->tr_filename))
449*aa772005SRobert Watson 		return (true);
450*aa772005SRobert Watson 	fd = dirfd(trail->tr_dirfp);
451*aa772005SRobert Watson 	PJDLOG_ASSERT(fd >= 0);
452*aa772005SRobert Watson 	if (faccessat(fd, trail->tr_filename, F_OK, 0) == 0)
453*aa772005SRobert Watson 		return (false);
454*aa772005SRobert Watson 	if (errno != ENOENT) {
455*aa772005SRobert Watson 		pjdlog_errno(LOG_ERR, "Unable to access file \"%s/%s\"",
456*aa772005SRobert Watson 		    trail->tr_dirname, trail->tr_filename);
457*aa772005SRobert Watson 	}
458*aa772005SRobert Watson 	strlcpy(filename, trail->tr_filename, sizeof(filename));
459*aa772005SRobert Watson 	if (!trail_find(trail)) {
460*aa772005SRobert Watson 		pjdlog_error("Trail file \"%s/%s\" disappeared.",
461*aa772005SRobert Watson 		    trail->tr_dirname, trail->tr_filename);
462*aa772005SRobert Watson 		return (true);
463*aa772005SRobert Watson 	}
464*aa772005SRobert Watson 	pjdlog_debug(1, "Trail file \"%s/%s\" was renamed to \"%s/%s\".",
465*aa772005SRobert Watson 	    trail->tr_dirname, filename, trail->tr_dirname,
466*aa772005SRobert Watson 	    trail->tr_filename);
467*aa772005SRobert Watson 	return (true);
468*aa772005SRobert Watson }
469*aa772005SRobert Watson 
470*aa772005SRobert Watson const char *
471*aa772005SRobert Watson trail_filename(const struct trail *trail)
472*aa772005SRobert Watson {
473*aa772005SRobert Watson 
474*aa772005SRobert Watson 	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
475*aa772005SRobert Watson 
476*aa772005SRobert Watson 	return (trail->tr_filename);
477*aa772005SRobert Watson }
478*aa772005SRobert Watson 
479*aa772005SRobert Watson int
480*aa772005SRobert Watson trail_filefd(const struct trail *trail)
481*aa772005SRobert Watson {
482*aa772005SRobert Watson 
483*aa772005SRobert Watson 	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
484*aa772005SRobert Watson 
485*aa772005SRobert Watson 	return (trail->tr_filefd);
486*aa772005SRobert Watson }
487*aa772005SRobert Watson 
488*aa772005SRobert Watson int
489*aa772005SRobert Watson trail_dirfd(const struct trail *trail)
490*aa772005SRobert Watson {
491*aa772005SRobert Watson 
492*aa772005SRobert Watson 	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
493*aa772005SRobert Watson 
494*aa772005SRobert Watson 	return (dirfd(trail->tr_dirfp));
495*aa772005SRobert Watson }
496*aa772005SRobert Watson 
497*aa772005SRobert Watson /*
498*aa772005SRobert Watson  * Find the last file in the directory opened under dirfp.
499*aa772005SRobert Watson  */
500*aa772005SRobert Watson void
501*aa772005SRobert Watson trail_last(DIR *dirfp, char *filename, size_t filenamesize)
502*aa772005SRobert Watson {
503*aa772005SRobert Watson 	char curfile[PATH_MAX];
504*aa772005SRobert Watson 	struct dirent *dp;
505*aa772005SRobert Watson 
506*aa772005SRobert Watson 	PJDLOG_ASSERT(dirfp != NULL);
507*aa772005SRobert Watson 
508*aa772005SRobert Watson 	curfile[0] = '\0';
509*aa772005SRobert Watson 
510*aa772005SRobert Watson 	rewinddir(dirfp);
511*aa772005SRobert Watson 	while ((dp = readdir(dirfp)) != NULL) {
512*aa772005SRobert Watson 		if (dp->d_name[0] < '0' || dp->d_name[0] > '9')
513*aa772005SRobert Watson 			continue;
514*aa772005SRobert Watson 		if (dp->d_type == DT_UNKNOWN)
515*aa772005SRobert Watson 			dp->d_type = trail_type(dirfp, dp->d_name);
516*aa772005SRobert Watson 		/* We are only interested in regular files, skip the rest. */
517*aa772005SRobert Watson 		if (dp->d_type != DT_REG)
518*aa772005SRobert Watson 			continue;
519*aa772005SRobert Watson 		/* Skip all files "greater" than curfile. */
520*aa772005SRobert Watson 		if (curfile[0] != '\0' && strcmp(dp->d_name, curfile) < 0)
521*aa772005SRobert Watson 			continue;
522*aa772005SRobert Watson 		PJDLOG_VERIFY(strlcpy(curfile, dp->d_name, sizeof(curfile)) <
523*aa772005SRobert Watson 		    sizeof(curfile));
524*aa772005SRobert Watson 	}
525*aa772005SRobert Watson 	if (curfile[0] == '\0') {
526*aa772005SRobert Watson 		/*
527*aa772005SRobert Watson 		 * There are no trail files, so we return.
528*aa772005SRobert Watson 		 */
529*aa772005SRobert Watson 		pjdlog_debug(1, "No trail files.");
530*aa772005SRobert Watson 		bzero(filename, filenamesize);
531*aa772005SRobert Watson 		return;
532*aa772005SRobert Watson 	}
533*aa772005SRobert Watson 	PJDLOG_VERIFY(strlcpy(filename, curfile, filenamesize) < filenamesize);
534*aa772005SRobert Watson 	pjdlog_debug(1, "Found the most recent trail file: \"%s\".", filename);
535*aa772005SRobert Watson }
536*aa772005SRobert Watson 
537*aa772005SRobert Watson /*
538*aa772005SRobert Watson  * Check if the given file name is a valid audit trail file name.
539*aa772005SRobert Watson  * Possible names:
540*aa772005SRobert Watson  * 20120106132657.20120106132805
541*aa772005SRobert Watson  * 20120106132657.not_terminated
542*aa772005SRobert Watson  * 20120106132657.crash_recovery
543*aa772005SRobert Watson  * If two names are given, check if the first name can be renamed
544*aa772005SRobert Watson  * to the second name. When renaming, first part of the name has
545*aa772005SRobert Watson  * to be identical and only the following renames are valid:
546*aa772005SRobert Watson  * 20120106132657.not_terminated -> 20120106132657.20120106132805
547*aa772005SRobert Watson  * 20120106132657.not_terminated -> 20120106132657.crash_recovery
548*aa772005SRobert Watson  */
549*aa772005SRobert Watson bool
550*aa772005SRobert Watson trail_validate_name(const char *srcname, const char *dstname)
551*aa772005SRobert Watson {
552*aa772005SRobert Watson 	int i;
553*aa772005SRobert Watson 
554*aa772005SRobert Watson 	PJDLOG_ASSERT(srcname != NULL);
555*aa772005SRobert Watson 
556*aa772005SRobert Watson 	if (strlen(srcname) != 2 * HALF_LEN + 1)
557*aa772005SRobert Watson 		return (false);
558*aa772005SRobert Watson 	if (srcname[HALF_LEN] != '.')
559*aa772005SRobert Watson 		return (false);
560*aa772005SRobert Watson 	for (i = 0; i < HALF_LEN; i++) {
561*aa772005SRobert Watson 		if (srcname[i] < '0' || srcname[i] > '9')
562*aa772005SRobert Watson 			return (false);
563*aa772005SRobert Watson 	}
564*aa772005SRobert Watson 	for (i = HALF_LEN + 1; i < 2 * HALF_LEN - 1; i++) {
565*aa772005SRobert Watson 		if (srcname[i] < '0' || srcname[i] > '9')
566*aa772005SRobert Watson 			break;
567*aa772005SRobert Watson 	}
568*aa772005SRobert Watson 	if (i < 2 * HALF_LEN - 1 &&
569*aa772005SRobert Watson 	    strcmp(srcname + HALF_LEN + 1, "not_terminated") != 0 &&
570*aa772005SRobert Watson 	    strcmp(srcname + HALF_LEN + 1, "crash_recovery") != 0) {
571*aa772005SRobert Watson 		return (false);
572*aa772005SRobert Watson 	}
573*aa772005SRobert Watson 
574*aa772005SRobert Watson 	if (dstname == NULL)
575*aa772005SRobert Watson 		return (true);
576*aa772005SRobert Watson 
577*aa772005SRobert Watson 	/* We tolarate if both names are identical. */
578*aa772005SRobert Watson 	if (strcmp(srcname, dstname) == 0)
579*aa772005SRobert Watson 		return (true);
580*aa772005SRobert Watson 
581*aa772005SRobert Watson 	/* We can only rename not_terminated files. */
582*aa772005SRobert Watson 	if (strcmp(srcname + HALF_LEN + 1, "not_terminated") != 0)
583*aa772005SRobert Watson 		return (false);
584*aa772005SRobert Watson 	if (strlen(dstname) != 2 * HALF_LEN + 1)
585*aa772005SRobert Watson 		return (false);
586*aa772005SRobert Watson 	if (strncmp(srcname, dstname, HALF_LEN + 1) != 0)
587*aa772005SRobert Watson 		return (false);
588*aa772005SRobert Watson 	for (i = HALF_LEN + 1; i < 2 * HALF_LEN - 1; i++) {
589*aa772005SRobert Watson 		if (dstname[i] < '0' || dstname[i] > '9')
590*aa772005SRobert Watson 			break;
591*aa772005SRobert Watson 	}
592*aa772005SRobert Watson 	if (i < 2 * HALF_LEN - 1 &&
593*aa772005SRobert Watson 	    strcmp(dstname + HALF_LEN + 1, "crash_recovery") != 0) {
594*aa772005SRobert Watson 		return (false);
595*aa772005SRobert Watson 	}
596*aa772005SRobert Watson 
597*aa772005SRobert Watson 	return (true);
598*aa772005SRobert Watson }
599*aa772005SRobert Watson 
600*aa772005SRobert Watson int
601*aa772005SRobert Watson trail_name_compare(const char *name0, const char *name1)
602*aa772005SRobert Watson {
603*aa772005SRobert Watson 	int ret;
604*aa772005SRobert Watson 
605*aa772005SRobert Watson 	ret = strcmp(name0, name1);
606*aa772005SRobert Watson 	if (ret == 0)
607*aa772005SRobert Watson 		return (TRAIL_IDENTICAL);
608*aa772005SRobert Watson 	if (strncmp(name0, name1, HALF_LEN + 1) == 0)
609*aa772005SRobert Watson 		return (TRAIL_RENAMED);
610*aa772005SRobert Watson 	return (ret < 0 ? TRAIL_OLDER : TRAIL_NEWER);
611*aa772005SRobert Watson }
612