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