1243730Srwatson/*-
2243730Srwatson * Copyright (c) 2012 The FreeBSD Foundation
3243730Srwatson * All rights reserved.
4243730Srwatson *
5243730Srwatson * This software was developed by Pawel Jakub Dawidek under sponsorship from
6243730Srwatson * the FreeBSD Foundation.
7243730Srwatson *
8243730Srwatson * Redistribution and use in source and binary forms, with or without
9243730Srwatson * modification, are permitted provided that the following conditions
10243730Srwatson * are met:
11243730Srwatson * 1. Redistributions of source code must retain the above copyright
12243730Srwatson *    notice, this list of conditions and the following disclaimer.
13243730Srwatson * 2. Redistributions in binary form must reproduce the above copyright
14243730Srwatson *    notice, this list of conditions and the following disclaimer in the
15243730Srwatson *    documentation and/or other materials provided with the distribution.
16243730Srwatson *
17243730Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18243730Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19243730Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20243730Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
21243730Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22243730Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23243730Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24243730Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25243730Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26243730Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27243730Srwatson * SUCH DAMAGE.
28243730Srwatson *
29243734Srwatson * $P4: //depot/projects/trustedbsd/openbsm/bin/auditdistd/trail.c#3 $
30243730Srwatson */
31243730Srwatson
32243734Srwatson#include <config/config.h>
33243730Srwatson
34243730Srwatson#include <sys/param.h>
35243730Srwatson#include <sys/stat.h>
36243730Srwatson
37243730Srwatson#include <dirent.h>
38243730Srwatson#include <errno.h>
39243730Srwatson#include <fcntl.h>
40243730Srwatson#include <stdbool.h>
41243730Srwatson#include <stdint.h>
42243730Srwatson#include <stdlib.h>
43243730Srwatson#include <string.h>
44243730Srwatson#include <unistd.h>
45243730Srwatson
46243730Srwatson#include <compat/compat.h>
47243730Srwatson#ifndef HAVE_STRLCPY
48243730Srwatson#include <compat/strlcpy.h>
49243730Srwatson#endif
50243730Srwatson#ifndef HAVE_FACCESSAT
51243730Srwatson#include "faccessat.h"
52243730Srwatson#endif
53243730Srwatson#ifndef HAVE_FSTATAT
54243730Srwatson#include "fstatat.h"
55243730Srwatson#endif
56243730Srwatson#ifndef HAVE_OPENAT
57243730Srwatson#include "openat.h"
58243730Srwatson#endif
59243730Srwatson#ifndef HAVE_UNLINKAT
60243730Srwatson#include "unlinkat.h"
61243730Srwatson#endif
62243730Srwatson
63243734Srwatson#include "pjdlog.h"
64243730Srwatson#include "trail.h"
65243730Srwatson
66243730Srwatson#define	TRAIL_MAGIC	0x79a11
67243730Srwatsonstruct trail {
68243730Srwatson	int	 tr_magic;
69243730Srwatson	/* Path usually to /var/audit/dist/ directory. */
70243730Srwatson	char	 tr_dirname[PATH_MAX];
71243730Srwatson	/* Descriptor to td_dirname directory. */
72243730Srwatson	DIR	*tr_dirfp;
73243730Srwatson	/* Path to audit trail file. */
74243730Srwatson	char	 tr_filename[PATH_MAX];
75243730Srwatson	/* Descriptor to audit trail file. */
76243730Srwatson	int	 tr_filefd;
77243730Srwatson};
78243730Srwatson
79243730Srwatson#define	HALF_LEN	14
80243730Srwatson
81243730Srwatsonbool
82243730Srwatsontrail_is_not_terminated(const char *filename)
83243730Srwatson{
84243730Srwatson
85243730Srwatson	return (strcmp(filename + HALF_LEN, ".not_terminated") == 0);
86243730Srwatson}
87243730Srwatson
88243730Srwatsonbool
89243730Srwatsontrail_is_crash_recovery(const char *filename)
90243730Srwatson{
91243730Srwatson
92243730Srwatson	return (strcmp(filename + HALF_LEN, ".crash_recovery") == 0);
93243730Srwatson}
94243730Srwatson
95243730Srwatsonstruct trail *
96243730Srwatsontrail_new(const char *dirname, bool create)
97243730Srwatson{
98243730Srwatson	struct trail *trail;
99243730Srwatson
100243730Srwatson	trail = calloc(1, sizeof(*trail));
101243730Srwatson
102243730Srwatson	if (strlcpy(trail->tr_dirname, dirname, sizeof(trail->tr_dirname)) >=
103243730Srwatson	    sizeof(trail->tr_dirname)) {
104243730Srwatson		free(trail);
105243730Srwatson		pjdlog_error("Directory name too long (\"%s\").", dirname);
106243730Srwatson		errno = ENAMETOOLONG;
107243730Srwatson		return (NULL);
108243730Srwatson	}
109243730Srwatson	trail->tr_dirfp = opendir(dirname);
110243730Srwatson	if (trail->tr_dirfp == NULL) {
111243730Srwatson		if (create && errno == ENOENT) {
112243730Srwatson			if (mkdir(dirname, 0700) == -1) {
113243730Srwatson				pjdlog_errno(LOG_ERR,
114243730Srwatson				    "Unable to create directory \"%s\"",
115243730Srwatson				    dirname);
116243730Srwatson				free(trail);
117243730Srwatson				return (NULL);
118243730Srwatson			}
119243730Srwatson			/* TODO: Set directory ownership. */
120243730Srwatson		} else {
121243730Srwatson			pjdlog_errno(LOG_ERR,
122243730Srwatson			    "Unable to open directory \"%s\"",
123243730Srwatson			    dirname);
124243730Srwatson			free(trail);
125243730Srwatson			return (NULL);
126243730Srwatson		}
127243730Srwatson		trail->tr_dirfp = opendir(dirname);
128243730Srwatson		if (trail->tr_dirfp == NULL) {
129243730Srwatson			pjdlog_errno(LOG_ERR,
130243730Srwatson			    "Unable to open directory \"%s\"",
131243730Srwatson			    dirname);
132243730Srwatson			free(trail);
133243730Srwatson			return (NULL);
134243730Srwatson		}
135243730Srwatson	}
136243730Srwatson	trail->tr_filefd = -1;
137243730Srwatson	trail->tr_magic = TRAIL_MAGIC;
138243730Srwatson	return (trail);
139243730Srwatson}
140243730Srwatson
141243730Srwatsonvoid
142243730Srwatsontrail_free(struct trail *trail)
143243730Srwatson{
144243730Srwatson
145243730Srwatson	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
146243730Srwatson
147243730Srwatson	if (trail->tr_filefd != -1)
148243730Srwatson		trail_close(trail);
149243730Srwatson	closedir(trail->tr_dirfp);
150243730Srwatson	bzero(trail, sizeof(*trail));
151243730Srwatson	trail->tr_magic = 0;
152243730Srwatson	trail->tr_filefd = -1;
153243730Srwatson	free(trail);
154243730Srwatson}
155243730Srwatson
156243730Srwatsonstatic uint8_t
157243730Srwatsontrail_type(DIR *dirfp, const char *filename)
158243730Srwatson{
159243730Srwatson	struct stat sb;
160243730Srwatson	int dfd;
161243730Srwatson
162243730Srwatson	PJDLOG_ASSERT(dirfp != NULL);
163243730Srwatson
164243730Srwatson	dfd = dirfd(dirfp);
165243730Srwatson	PJDLOG_ASSERT(dfd >= 0);
166243730Srwatson	if (fstatat(dfd, filename, &sb, AT_SYMLINK_NOFOLLOW) == -1) {
167243730Srwatson		pjdlog_errno(LOG_ERR, "Unable to stat \"%s\"", filename);
168243730Srwatson		return (DT_UNKNOWN);
169243730Srwatson	}
170243730Srwatson	return (IFTODT(sb.st_mode));
171243730Srwatson}
172243730Srwatson
173243730Srwatson/*
174243730Srwatson * Find trail file by first part of the name in case it was renamed.
175243730Srwatson * First part of the trail file name never changes, but trail file
176243730Srwatson * can be renamed when hosts are disconnected from .not_terminated
177243730Srwatson * to .[0-9]{14} or to .crash_recovery.
178243730Srwatson */
179243730Srwatsonstatic bool
180243730Srwatsontrail_find(struct trail *trail)
181243730Srwatson{
182243730Srwatson	struct dirent *dp;
183243730Srwatson
184243730Srwatson	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
185243730Srwatson	PJDLOG_ASSERT(trail_is_not_terminated(trail->tr_filename));
186243730Srwatson
187243730Srwatson	rewinddir(trail->tr_dirfp);
188243730Srwatson	while ((dp = readdir(trail->tr_dirfp)) != NULL) {
189243730Srwatson		if (strncmp(dp->d_name, trail->tr_filename, HALF_LEN + 1) == 0)
190243730Srwatson			break;
191243730Srwatson	}
192243730Srwatson	if (dp == NULL)
193243730Srwatson		return (false);
194243730Srwatson	PJDLOG_VERIFY(strlcpy(trail->tr_filename, dp->d_name,
195243730Srwatson	    sizeof(trail->tr_filename)) < sizeof(trail->tr_filename));
196243730Srwatson	return (true);
197243730Srwatson}
198243730Srwatson
199243730Srwatson/*
200243730Srwatson * Open the given trail file and move pointer at the given offset, as this is
201243730Srwatson * where receiver finished the last time.
202243730Srwatson * If the file doesn't exist or the given offset is equal to the file size,
203243730Srwatson * move to the next trail file.
204243730Srwatson */
205243730Srwatsonvoid
206243730Srwatsontrail_start(struct trail *trail, const char *filename, off_t offset)
207243730Srwatson{
208243730Srwatson	struct stat sb;
209243730Srwatson	int dfd, fd;
210243730Srwatson
211243730Srwatson	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
212243730Srwatson
213243730Srwatson	PJDLOG_VERIFY(strlcpy(trail->tr_filename, filename,
214243730Srwatson	    sizeof(trail->tr_filename)) < sizeof(trail->tr_filename));
215243730Srwatson	trail->tr_filefd = -1;
216243730Srwatson
217243730Srwatson	if (trail->tr_filename[0] == '\0') {
218243730Srwatson		PJDLOG_ASSERT(offset == 0);
219243730Srwatson		trail_next(trail);
220243730Srwatson		return;
221243730Srwatson	}
222243730Srwatson
223243730Srwatson	dfd = dirfd(trail->tr_dirfp);
224243730Srwatson	PJDLOG_ASSERT(dfd >= 0);
225243730Srwatsonagain:
226243730Srwatson	fd = openat(dfd, trail->tr_filename, O_RDONLY);
227243730Srwatson	if (fd == -1) {
228243730Srwatson		if (errno == ENOENT &&
229243730Srwatson		    trail_is_not_terminated(trail->tr_filename) &&
230243730Srwatson		    trail_find(trail)) {
231243730Srwatson			/* File was renamed. Retry with new name. */
232243730Srwatson			pjdlog_debug(1,
233243730Srwatson			   "Trail file was renamed since last connection to \"%s/%s\".",
234243730Srwatson			   trail->tr_dirname, trail->tr_filename);
235243730Srwatson			goto again;
236243730Srwatson		} else if (errno == ENOENT) {
237243730Srwatson			/* File disappeared. */
238243730Srwatson			pjdlog_debug(1, "File \"%s/%s\" doesn't exist.",
239243730Srwatson			    trail->tr_dirname, trail->tr_filename);
240243730Srwatson		} else {
241243730Srwatson			pjdlog_errno(LOG_ERR,
242243730Srwatson			    "Unable to open file \"%s/%s\", skipping",
243243730Srwatson			    trail->tr_dirname, trail->tr_filename);
244243730Srwatson		}
245243730Srwatson		trail_next(trail);
246243730Srwatson		return;
247243730Srwatson	}
248243730Srwatson	if (fstat(fd, &sb) == -1) {
249243730Srwatson		pjdlog_errno(LOG_ERR,
250243730Srwatson		    "Unable to stat file \"%s/%s\", skipping",
251243730Srwatson		    trail->tr_dirname, trail->tr_filename);
252243730Srwatson		close(fd);
253243730Srwatson		trail_next(trail);
254243730Srwatson		return;
255243730Srwatson	}
256243730Srwatson	if (!S_ISREG(sb.st_mode)) {
257243730Srwatson		pjdlog_warning("File \"%s/%s\" is not a regular file, skipping.",
258243730Srwatson		    trail->tr_dirname, trail->tr_filename);
259243730Srwatson		close(fd);
260243730Srwatson		trail_next(trail);
261243730Srwatson		return;
262243730Srwatson	}
263243730Srwatson	/*
264243730Srwatson	 * We continue sending requested file if:
265243730Srwatson	 * 1. It is not fully sent yet, or
266243730Srwatson	 * 2. It is fully sent, but is not terminated, so new data can be
267243730Srwatson	 *    appended still, or
268243730Srwatson	 * 3. It is fully sent but file name has changed.
269243730Srwatson	 *
270243730Srwatson	 * Note that we are fine if our .not_terminated or .crash_recovery file
271243730Srwatson	 * is smaller than the one on the receiver side, as it is possible that
272243730Srwatson	 * more data was send to the receiver than was safely stored on disk.
273243730Srwatson	 * We accept .not_terminated only because auditdistd can start before
274243730Srwatson	 * auditd manage to rename it to .crash_recovery.
275243730Srwatson	 */
276243730Srwatson	if (offset < sb.st_size ||
277243730Srwatson	    (offset >= sb.st_size &&
278243730Srwatson	     trail_is_not_terminated(trail->tr_filename)) ||
279243730Srwatson	    (offset >= sb.st_size && trail_is_not_terminated(filename) &&
280243730Srwatson	     trail_is_crash_recovery(trail->tr_filename))) {
281243730Srwatson		/* File was not fully send. Let's finish it. */
282243730Srwatson		if (lseek(fd, offset, SEEK_SET) == -1) {
283243730Srwatson			pjdlog_errno(LOG_ERR,
284243730Srwatson			    "Unable to move to offset %jd within file \"%s/%s\", skipping",
285243730Srwatson			    (intmax_t)offset, trail->tr_dirname,
286243730Srwatson			    trail->tr_filename);
287243730Srwatson			close(fd);
288243730Srwatson			trail_next(trail);
289243730Srwatson			return;
290243730Srwatson		}
291243730Srwatson		if (!trail_is_crash_recovery(trail->tr_filename)) {
292243730Srwatson			pjdlog_debug(1,
293243730Srwatson			    "Restarting file \"%s/%s\" at offset %jd.",
294243730Srwatson			    trail->tr_dirname, trail->tr_filename,
295243730Srwatson			    (intmax_t)offset);
296243730Srwatson		}
297243730Srwatson		trail->tr_filefd = fd;
298243730Srwatson		return;
299243730Srwatson	}
300243730Srwatson	close(fd);
301243730Srwatson	if (offset > sb.st_size) {
302243730Srwatson		pjdlog_warning("File \"%s/%s\" shrinked, removing it.",
303243730Srwatson		    trail->tr_dirname, trail->tr_filename);
304243730Srwatson	} else {
305243730Srwatson		pjdlog_debug(1, "File \"%s/%s\" is already sent, removing it.",
306243730Srwatson		    trail->tr_dirname, trail->tr_filename);
307243730Srwatson	}
308243730Srwatson	/* Entire file is already sent or it shirnked, we can remove it. */
309243730Srwatson	if (unlinkat(dfd, trail->tr_filename, 0) == -1) {
310243730Srwatson		pjdlog_errno(LOG_WARNING, "Unable to remove file \"%s/%s\"",
311243730Srwatson		    trail->tr_dirname, trail->tr_filename);
312243730Srwatson	}
313243730Srwatson	trail_next(trail);
314243730Srwatson}
315243730Srwatson
316243730Srwatson/*
317243730Srwatson * Set next file in the trail->tr_dirname directory and open it for reading.
318243730Srwatson */
319243730Srwatsonvoid
320243730Srwatsontrail_next(struct trail *trail)
321243730Srwatson{
322243730Srwatson	char curfile[PATH_MAX];
323243730Srwatson	struct dirent *dp;
324243730Srwatson	int dfd;
325243730Srwatson
326243730Srwatson	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
327243730Srwatson	PJDLOG_ASSERT(trail->tr_filefd == -1);
328243730Srwatson
329243730Srwatsonagain:
330243730Srwatson	curfile[0] = '\0';
331243730Srwatson
332243730Srwatson	rewinddir(trail->tr_dirfp);
333243730Srwatson	while ((dp = readdir(trail->tr_dirfp)) != NULL) {
334243730Srwatson		if (dp->d_name[0] < '0' || dp->d_name[0] > '9')
335243730Srwatson			continue;
336243730Srwatson		if (dp->d_type == DT_UNKNOWN)
337243730Srwatson			dp->d_type = trail_type(trail->tr_dirfp, dp->d_name);
338243730Srwatson		/* We are only interested in regular files, skip the rest. */
339243730Srwatson		if (dp->d_type != DT_REG) {
340243730Srwatson			pjdlog_debug(1,
341243730Srwatson			    "File \"%s/%s\" is not a regular file, skipping.",
342243730Srwatson			    trail->tr_dirname, dp->d_name);
343243730Srwatson			continue;
344243730Srwatson		}
345243730Srwatson		/* Skip all files "greater" than curfile. */
346243730Srwatson		if (curfile[0] != '\0' && strcmp(dp->d_name, curfile) > 0)
347243730Srwatson			continue;
348243730Srwatson		/* Skip all files "smaller" than the current trail_filename. */
349243730Srwatson		if (trail->tr_filename[0] != '\0' &&
350243730Srwatson		    strcmp(dp->d_name, trail->tr_filename) <= 0) {
351243730Srwatson			continue;
352243730Srwatson		}
353243730Srwatson		PJDLOG_VERIFY(strlcpy(curfile, dp->d_name, sizeof(curfile)) <
354243730Srwatson		    sizeof(curfile));
355243730Srwatson	}
356243730Srwatson	if (curfile[0] == '\0') {
357243730Srwatson		/*
358243730Srwatson		 * There are no new trail files, so we return.
359243730Srwatson		 * We don't clear trail_filename string, to know where to
360243730Srwatson		 * start when new file appears.
361243730Srwatson		 */
362243730Srwatson		PJDLOG_ASSERT(trail->tr_filefd == -1);
363243730Srwatson		pjdlog_debug(1, "No new trail files.");
364243730Srwatson		return;
365243730Srwatson	}
366243730Srwatson	PJDLOG_VERIFY(strlcpy(trail->tr_filename, curfile,
367243730Srwatson	    sizeof(trail->tr_filename)) < sizeof(trail->tr_filename));
368243730Srwatson	dfd = dirfd(trail->tr_dirfp);
369243730Srwatson	PJDLOG_ASSERT(dfd >= 0);
370243730Srwatson	trail->tr_filefd = openat(dfd, trail->tr_filename, O_RDONLY);
371243730Srwatson	if (trail->tr_filefd == -1) {
372243730Srwatson		pjdlog_errno(LOG_ERR,
373243730Srwatson		    "Unable to open file \"%s/%s\", skipping",
374243730Srwatson		    trail->tr_dirname, trail->tr_filename);
375243730Srwatson		goto again;
376243730Srwatson	}
377243730Srwatson	pjdlog_debug(1, "Found next trail file: \"%s/%s\".", trail->tr_dirname,
378243730Srwatson	    trail->tr_filename);
379243730Srwatson}
380243730Srwatson
381243730Srwatson/*
382243730Srwatson * Close current trial file.
383243730Srwatson */
384243730Srwatsonvoid
385243730Srwatsontrail_close(struct trail *trail)
386243730Srwatson{
387243730Srwatson
388243730Srwatson	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
389243730Srwatson	PJDLOG_ASSERT(trail->tr_filefd >= 0);
390243730Srwatson	PJDLOG_ASSERT(trail->tr_filename[0] != '\0');
391243730Srwatson
392243730Srwatson	PJDLOG_VERIFY(close(trail->tr_filefd) == 0);
393243730Srwatson	trail->tr_filefd = -1;
394243730Srwatson}
395243730Srwatson
396243730Srwatson/*
397243730Srwatson * Reset trail state. Used when connection is disconnected and we will
398243730Srwatson * need to start over after reconnect. Trail needs to be already closed.
399243730Srwatson */
400243730Srwatsonvoid
401243730Srwatsontrail_reset(struct trail *trail)
402243730Srwatson{
403243730Srwatson
404243730Srwatson	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
405243730Srwatson	PJDLOG_ASSERT(trail->tr_filefd == -1);
406243730Srwatson
407243730Srwatson	trail->tr_filename[0] = '\0';
408243730Srwatson}
409243730Srwatson
410243730Srwatson/*
411243730Srwatson * Unlink current trial file.
412243730Srwatson */
413243730Srwatsonvoid
414243730Srwatsontrail_unlink(struct trail *trail, const char *filename)
415243730Srwatson{
416243730Srwatson	int dfd;
417243730Srwatson
418243730Srwatson	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
419243730Srwatson	PJDLOG_ASSERT(filename != NULL);
420243730Srwatson	PJDLOG_ASSERT(filename[0] != '\0');
421243730Srwatson
422243730Srwatson	dfd = dirfd(trail->tr_dirfp);
423243730Srwatson	PJDLOG_ASSERT(dfd >= 0);
424243730Srwatson
425243730Srwatson	if (unlinkat(dfd, filename, 0) == -1) {
426243730Srwatson		pjdlog_errno(LOG_ERR, "Unable to remove \"%s/%s\"",
427243730Srwatson		    trail->tr_dirname, filename);
428243730Srwatson	} else {
429243730Srwatson		pjdlog_debug(1, "Trail file \"%s/%s\" removed.",
430243730Srwatson		    trail->tr_dirname, filename);
431243730Srwatson	}
432243730Srwatson}
433243730Srwatson
434243730Srwatson/*
435243730Srwatson * Return true if we should switch to next trail file.
436243730Srwatson * We don't switch if our file name ends with ".not_terminated" and it
437243730Srwatson * exists (ie. wasn't renamed).
438243730Srwatson */
439243730Srwatsonbool
440243730Srwatsontrail_switch(struct trail *trail)
441243730Srwatson{
442243730Srwatson	char filename[PATH_MAX];
443243730Srwatson	int fd;
444243730Srwatson
445243730Srwatson	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
446243730Srwatson	PJDLOG_ASSERT(trail->tr_filefd >= 0);
447243730Srwatson
448243730Srwatson	if (!trail_is_not_terminated(trail->tr_filename))
449243730Srwatson		return (true);
450243730Srwatson	fd = dirfd(trail->tr_dirfp);
451243730Srwatson	PJDLOG_ASSERT(fd >= 0);
452243730Srwatson	if (faccessat(fd, trail->tr_filename, F_OK, 0) == 0)
453243730Srwatson		return (false);
454243730Srwatson	if (errno != ENOENT) {
455243730Srwatson		pjdlog_errno(LOG_ERR, "Unable to access file \"%s/%s\"",
456243730Srwatson		    trail->tr_dirname, trail->tr_filename);
457243730Srwatson	}
458243730Srwatson	strlcpy(filename, trail->tr_filename, sizeof(filename));
459243730Srwatson	if (!trail_find(trail)) {
460243730Srwatson		pjdlog_error("Trail file \"%s/%s\" disappeared.",
461243730Srwatson		    trail->tr_dirname, trail->tr_filename);
462243730Srwatson		return (true);
463243730Srwatson	}
464243730Srwatson	pjdlog_debug(1, "Trail file \"%s/%s\" was renamed to \"%s/%s\".",
465243730Srwatson	    trail->tr_dirname, filename, trail->tr_dirname,
466243730Srwatson	    trail->tr_filename);
467243730Srwatson	return (true);
468243730Srwatson}
469243730Srwatson
470243730Srwatsonconst char *
471243730Srwatsontrail_filename(const struct trail *trail)
472243730Srwatson{
473243730Srwatson
474243730Srwatson	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
475243730Srwatson
476243730Srwatson	return (trail->tr_filename);
477243730Srwatson}
478243730Srwatson
479243730Srwatsonint
480243730Srwatsontrail_filefd(const struct trail *trail)
481243730Srwatson{
482243730Srwatson
483243730Srwatson	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
484243730Srwatson
485243730Srwatson	return (trail->tr_filefd);
486243730Srwatson}
487243730Srwatson
488243730Srwatsonint
489243730Srwatsontrail_dirfd(const struct trail *trail)
490243730Srwatson{
491243730Srwatson
492243730Srwatson	PJDLOG_ASSERT(trail->tr_magic == TRAIL_MAGIC);
493243730Srwatson
494243730Srwatson	return (dirfd(trail->tr_dirfp));
495243730Srwatson}
496243730Srwatson
497243730Srwatson/*
498243730Srwatson * Find the last file in the directory opened under dirfp.
499243730Srwatson */
500243730Srwatsonvoid
501243730Srwatsontrail_last(DIR *dirfp, char *filename, size_t filenamesize)
502243730Srwatson{
503243730Srwatson	char curfile[PATH_MAX];
504243730Srwatson	struct dirent *dp;
505243730Srwatson
506243730Srwatson	PJDLOG_ASSERT(dirfp != NULL);
507243730Srwatson
508243730Srwatson	curfile[0] = '\0';
509243730Srwatson
510243730Srwatson	rewinddir(dirfp);
511243730Srwatson	while ((dp = readdir(dirfp)) != NULL) {
512243730Srwatson		if (dp->d_name[0] < '0' || dp->d_name[0] > '9')
513243730Srwatson			continue;
514243730Srwatson		if (dp->d_type == DT_UNKNOWN)
515243730Srwatson			dp->d_type = trail_type(dirfp, dp->d_name);
516243730Srwatson		/* We are only interested in regular files, skip the rest. */
517243730Srwatson		if (dp->d_type != DT_REG)
518243730Srwatson			continue;
519243730Srwatson		/* Skip all files "greater" than curfile. */
520243730Srwatson		if (curfile[0] != '\0' && strcmp(dp->d_name, curfile) < 0)
521243730Srwatson			continue;
522243730Srwatson		PJDLOG_VERIFY(strlcpy(curfile, dp->d_name, sizeof(curfile)) <
523243730Srwatson		    sizeof(curfile));
524243730Srwatson	}
525243730Srwatson	if (curfile[0] == '\0') {
526243730Srwatson		/*
527243730Srwatson		 * There are no trail files, so we return.
528243730Srwatson		 */
529243730Srwatson		pjdlog_debug(1, "No trail files.");
530243730Srwatson		bzero(filename, filenamesize);
531243730Srwatson		return;
532243730Srwatson	}
533243730Srwatson	PJDLOG_VERIFY(strlcpy(filename, curfile, filenamesize) < filenamesize);
534243730Srwatson	pjdlog_debug(1, "Found the most recent trail file: \"%s\".", filename);
535243730Srwatson}
536243730Srwatson
537243730Srwatson/*
538243730Srwatson * Check if the given file name is a valid audit trail file name.
539243730Srwatson * Possible names:
540243730Srwatson * 20120106132657.20120106132805
541243730Srwatson * 20120106132657.not_terminated
542243730Srwatson * 20120106132657.crash_recovery
543243730Srwatson * If two names are given, check if the first name can be renamed
544243730Srwatson * to the second name. When renaming, first part of the name has
545243730Srwatson * to be identical and only the following renames are valid:
546243730Srwatson * 20120106132657.not_terminated -> 20120106132657.20120106132805
547243730Srwatson * 20120106132657.not_terminated -> 20120106132657.crash_recovery
548243730Srwatson */
549243730Srwatsonbool
550243730Srwatsontrail_validate_name(const char *srcname, const char *dstname)
551243730Srwatson{
552243730Srwatson	int i;
553243730Srwatson
554243730Srwatson	PJDLOG_ASSERT(srcname != NULL);
555243730Srwatson
556243730Srwatson	if (strlen(srcname) != 2 * HALF_LEN + 1)
557243730Srwatson		return (false);
558243730Srwatson	if (srcname[HALF_LEN] != '.')
559243730Srwatson		return (false);
560243730Srwatson	for (i = 0; i < HALF_LEN; i++) {
561243730Srwatson		if (srcname[i] < '0' || srcname[i] > '9')
562243730Srwatson			return (false);
563243730Srwatson	}
564243730Srwatson	for (i = HALF_LEN + 1; i < 2 * HALF_LEN - 1; i++) {
565243730Srwatson		if (srcname[i] < '0' || srcname[i] > '9')
566243730Srwatson			break;
567243730Srwatson	}
568243730Srwatson	if (i < 2 * HALF_LEN - 1 &&
569243730Srwatson	    strcmp(srcname + HALF_LEN + 1, "not_terminated") != 0 &&
570243730Srwatson	    strcmp(srcname + HALF_LEN + 1, "crash_recovery") != 0) {
571243730Srwatson		return (false);
572243730Srwatson	}
573243730Srwatson
574243730Srwatson	if (dstname == NULL)
575243730Srwatson		return (true);
576243730Srwatson
577243730Srwatson	/* We tolarate if both names are identical. */
578243730Srwatson	if (strcmp(srcname, dstname) == 0)
579243730Srwatson		return (true);
580243730Srwatson
581243730Srwatson	/* We can only rename not_terminated files. */
582243730Srwatson	if (strcmp(srcname + HALF_LEN + 1, "not_terminated") != 0)
583243730Srwatson		return (false);
584243730Srwatson	if (strlen(dstname) != 2 * HALF_LEN + 1)
585243730Srwatson		return (false);
586243730Srwatson	if (strncmp(srcname, dstname, HALF_LEN + 1) != 0)
587243730Srwatson		return (false);
588243730Srwatson	for (i = HALF_LEN + 1; i < 2 * HALF_LEN - 1; i++) {
589243730Srwatson		if (dstname[i] < '0' || dstname[i] > '9')
590243730Srwatson			break;
591243730Srwatson	}
592243730Srwatson	if (i < 2 * HALF_LEN - 1 &&
593243730Srwatson	    strcmp(dstname + HALF_LEN + 1, "crash_recovery") != 0) {
594243730Srwatson		return (false);
595243730Srwatson	}
596243730Srwatson
597243730Srwatson	return (true);
598243730Srwatson}
599243730Srwatson
600243730Srwatsonint
601243730Srwatsontrail_name_compare(const char *name0, const char *name1)
602243730Srwatson{
603243730Srwatson	int ret;
604243730Srwatson
605243730Srwatson	ret = strcmp(name0, name1);
606243730Srwatson	if (ret == 0)
607243730Srwatson		return (TRAIL_IDENTICAL);
608243730Srwatson	if (strncmp(name0, name1, HALF_LEN + 1) == 0)
609243730Srwatson		return (TRAIL_RENAMED);
610243730Srwatson	return (ret < 0 ? TRAIL_OLDER : TRAIL_NEWER);
611243730Srwatson}
612