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