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