2 * Copyright (c) 2000 Silicon Graphics, Inc.
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation.
9 * This program is distributed in the hope that it would be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 * This module contains code for logging writes to files, and for
20 * perusing the resultant logfile. The main intent of all this is
21 * to provide a 'write history' of a file which can be examined to
22 * judge the state of a file (ie. whether it is corrupted or not) based
23 * on the write activity.
25 * The main abstractions available to the user are the wlog_file, and
26 * the wlog_rec. A wlog_file is a handle encapsulating a write logfile.
27 * It is initialized with the wlog_open() function. This handle is
28 * then passed to the various wlog_xxx() functions to provide transparent
29 * access to the write logfile.
31 * The wlog_rec datatype is a structure which contains all the information
32 * about a file write. Examples include the file name, offset, length,
33 * pattern, etc. In addition there is a bit which is cleared/set based
34 * on whether or not the write has been confirmed as complete. This
35 * allows the write logfile to contain information on writes which have
36 * been initiated, but not yet completed (as in async io).
38 * There is also a function to scan a write logfile in reverse order.
40 * NOTE: For target file analysis based on a write logfile, the
41 * assumption is made that the file being written to is
42 * locked from simultaneous access, so that the order of
43 * write completion is predictable. This is an issue when
44 * more than 1 process is trying to write data to the same
45 * target file simultaneously.
47 * The history file created is a collection of variable length records
48 * described by scruct wlog_rec_disk in write_log.h. See that module for
49 * the layout of the data on disk.
58 #include <sys/param.h>
60 #include <sys/types.h>
61 #include "write_log.h"
65 #define BSIZE DEV_BSIZE
73 /*#define PATH_MAX pathconf("/", _PC_PATH_MAX)*/
76 #define ERROR_STRING_LEN 1280
77 char Wlog_Error_String[ERROR_STRING_LEN];
80 static int wlog_rec_pack(struct wlog_rec *wrec, char *buf, int flag);
81 static int wlog_rec_unpack(struct wlog_rec *wrec, char *buf);
83 static int wlog_rec_pack();
84 static int wlog_rec_unpack();
88 * Initialize a write logfile. wfile is a wlog_file structure that has
89 * the w_file field filled in. The rest of the information in the
90 * structure is initialized by the routine.
92 * The trunc flag is used to indicate whether or not the logfile should
93 * be truncated if it currently exists. If it is non-zero, the file will
94 * be truncated, otherwise it will be appended to.
96 * The mode argument is the [absolute] mode which the file will be
97 * given if it does not exist. This mode is not affected by your process
102 wlog_open(wfile, trunc, mode)
103 struct wlog_file *wfile;
115 * Open 1 file descriptor as O_APPEND
118 oflags = O_WRONLY | O_APPEND | O_CREAT | trunc;
120 open(wfile->w_file, oflags, mode);
123 if (wfile->w_afd == -1) {
124 snprintf(Wlog_Error_String, ERROR_STRING_LEN,
125 "Could not open write_log - open(%s, %#o, %#o) failed: %s\n",
126 wfile->w_file, oflags, mode, strerror(errno));
131 * Open the next fd as a random access descriptor
135 if ((wfile->w_rfd = open(wfile->w_file, oflags)) == -1) {
136 snprintf(Wlog_Error_String, ERROR_STRING_LEN,
137 "Could not open write log - open(%s, %#o) failed: %s\n",
138 wfile->w_file, oflags, strerror(errno));
148 * Release all resources associated with a wlog_file structure allocated
149 * with the wlog_open() call.
154 struct wlog_file *wfile;
162 * Write a wlog_rec structure to a write logfile. Offset is used to
163 * control where the record will be written. If offset is < 0, the
164 * record will be appended to the end of the logfile. Otherwise, the
165 * record which exists at the indicated offset will be overlayed. This
166 * is so that we can record writes which are outstanding (with the w_done
167 * bit in wrec cleared), but not completed, and then later update the
168 * logfile when the write request completes (as with async io). When
169 * offset is >= 0, only the fixed length portion of the record is
170 * rewritten. See text in write_log.h for details on the format of an
173 * The return value of the function is the byte offset in the logfile
174 * where the record begins.
176 * Note: It is the callers responsibility to make sure that the offset
177 * parameter 'points' to a valid record location when a record is to be
178 * overlayed. This is guarenteed by saving the return value of a previous
179 * call to wlog_record_write() which wrote the record to be overlayed.
181 * Note2: The on-disk version of the wlog_rec is MUCH different than
182 * the user version. Don't expect to od the logfile and see data formatted
183 * as it is in the wlog_rec structure. Considerable data packing takes
184 * place before the record is written.
188 wlog_record_write(wfile, wrec, offset)
189 struct wlog_file *wfile;
190 struct wlog_rec *wrec;
194 char wbuf[WLOG_REC_MAX_SIZE + 2];
197 * If offset is -1, we append the record at the end of file
199 * Otherwise, we overlay wrec at the file offset indicated and assume
200 * that the caller passed us the correct offset. We do not record the
201 * fname in this case.
204 reclen = wlog_rec_pack(wrec, wbuf, (offset < 0));
208 * Since we're writing a complete new record, we must also tack
209 * its length onto the end so that wlog_scan_backward() will work.
210 * Length is asumed to fit into 2 bytes.
213 wbuf[reclen] = reclen / 256;
214 wbuf[reclen+1] = reclen % 256;
217 write(wfile->w_afd, wbuf, reclen);
218 offset = lseek(wfile->w_afd, 0, SEEK_CUR) - reclen;
220 lseek(wfile->w_rfd, offset, SEEK_SET);
221 write(wfile->w_rfd, wbuf, reclen);
228 * Function to scan a logfile in reverse order. Wfile is a valid
229 * wlog_file structure initialized by wlog_open(). nrecs is the number
230 * of records to scan (all records are scanned if nrecs is 0). func is
231 * a user-supplied function to call for each record found. The function
232 * will be passed a single parameter - a wlog_rec structure .
236 wlog_scan_backward(wfile, nrecs, func, data)
237 struct wlog_file *wfile;
242 int fd, leftover, nbytes, offset, recnum, reclen;
243 char buf[BSIZE*32], *bufend, *cp, *bufstart;
244 char albuf[WLOG_REC_MAX_SIZE];
245 struct wlog_rec wrec;
250 * Move to EOF. offset will always hold the current file offset
253 lseek(fd, 0, SEEK_END);
254 offset = lseek(fd, 0, SEEK_CUR);
256 bufend = buf + sizeof(buf);
261 while ((!nrecs || recnum < nrecs) && offset > 0) {
263 * Check for beginning of file - if there aren't enough bytes
264 * remaining to fill buf, adjust bufstart.
267 if (offset + leftover < sizeof(buf)) {
268 bufstart = bufend - (offset + leftover);
271 offset -= sizeof(buf) - leftover;
275 * Move to the proper file offset, and read into buf
278 lseek(fd, offset, SEEK_SET);
279 nbytes = read(fd, bufstart, bufend - bufstart - leftover);
282 sprintf(Wlog_Error_String,
283 "Could not read history file at offset %d - read(%d, %p, %d) failed: %s\n",
284 offset, fd, bufstart,
285 (int)(bufend - bufstart - leftover), strerror(errno));
292 while (cp >= bufstart) {
295 * If cp-bufstart is not large enough to hold a piece
296 * of record length information, copy remainder to end
297 * of buf and continue reading the file.
300 if (cp - bufstart < 2) {
301 leftover = cp - bufstart;
302 memcpy(bufend - leftover, bufstart, leftover);
307 * Extract the record length. We must do it this way
308 * instead of casting cp to an int because cp might
309 * not be word aligned.
312 reclen = (*(cp-2) * 256) + *(cp -1);
315 * If cp-bufstart isn't large enough to hold a
316 * complete record, plus the length information, copy
317 * the leftover bytes to the end of buf and continue
321 if (cp - bufstart < reclen + 2) {
322 leftover = cp - bufstart;
323 memcpy(bufend - leftover, bufstart, leftover);
328 * Adjust cp to point at the start of the record.
329 * Copy the record into wbuf so that it is word
330 * aligned and pass the record to the user supplied
335 memcpy(albuf, cp, reclen);
337 wlog_rec_unpack(&wrec, albuf);
340 * Call the user supplied function -
341 * stop if instructed to.
344 if ((*func)(&wrec, data) == WLOG_STOP_SCAN) {
350 if (nrecs && recnum >= nrecs)
359 * The following 2 routines are used to pack and unpack the user
360 * visible wlog_rec structure to/from a character buffer which is
361 * stored or read from the write logfile. Any changes to either of
362 * these routines must be reflected in the other.
366 wlog_rec_pack(wrec, buf, flag)
367 struct wlog_rec *wrec;
371 char *file, *host, *pattern;
372 struct wlog_rec_disk *wrecd;
374 wrecd = (struct wlog_rec_disk *)buf;
376 wrecd->w_pid = (uint)wrec->w_pid;
377 wrecd->w_offset = (uint)wrec->w_offset;
378 wrecd->w_nbytes = (uint)wrec->w_nbytes;
379 wrecd->w_oflags = (uint)wrec->w_oflags;
380 wrecd->w_done = (uint)wrec->w_done;
381 wrecd->w_async = (uint)wrec->w_async;
383 wrecd->w_pathlen = (wrec->w_pathlen > 0) ? (uint)wrec->w_pathlen : 0;
384 wrecd->w_hostlen = (wrec->w_hostlen > 0) ? (uint)wrec->w_hostlen : 0;
385 wrecd->w_patternlen = (wrec->w_patternlen > 0) ? (uint)wrec->w_patternlen : 0;
388 * If flag is true, we should also pack the variable length parts
389 * of the wlog_rec. By default, we only pack the fixed length
394 file = buf + sizeof(struct wlog_rec_disk);
395 host = file + wrecd->w_pathlen;
396 pattern = host + wrecd->w_hostlen;
398 if (wrecd->w_pathlen > 0)
399 memcpy(file, wrec->w_path, wrecd->w_pathlen);
401 if (wrecd->w_hostlen > 0)
402 memcpy(host, wrec->w_host, wrecd->w_hostlen);
404 if (wrecd->w_patternlen > 0)
405 memcpy(pattern, wrec->w_pattern, wrecd->w_patternlen);
407 return (sizeof(struct wlog_rec_disk) +
408 wrecd->w_pathlen + wrecd->w_hostlen + wrecd->w_patternlen);
410 return sizeof(struct wlog_rec_disk);
415 wlog_rec_unpack(wrec, buf)
416 struct wlog_rec *wrec;
419 char *file, *host, *pattern;
420 struct wlog_rec_disk *wrecd;
422 bzero((char *)wrec, sizeof(struct wlog_rec));
423 wrecd = (struct wlog_rec_disk *)buf;
425 wrec->w_pid = wrecd->w_pid;
426 wrec->w_offset = wrecd->w_offset;
427 wrec->w_nbytes = wrecd->w_nbytes;
428 wrec->w_oflags = wrecd->w_oflags;
429 wrec->w_hostlen = wrecd->w_hostlen;
430 wrec->w_pathlen = wrecd->w_pathlen;
431 wrec->w_patternlen = wrecd->w_patternlen;
432 wrec->w_done = wrecd->w_done;
433 wrec->w_async = wrecd->w_async;
435 if (wrec->w_pathlen > 0) {
436 file = buf + sizeof(struct wlog_rec_disk);
437 memcpy(wrec->w_path, file, wrec->w_pathlen);
440 if (wrec->w_hostlen > 0) {
441 host = buf + sizeof(struct wlog_rec_disk) + wrec->w_pathlen;
442 memcpy(wrec->w_host, host, wrec->w_hostlen);
445 if (wrec->w_patternlen > 0) {
446 pattern = buf + sizeof(struct wlog_rec_disk) +
447 wrec->w_pathlen + wrec->w_hostlen;
448 memcpy(wrec->w_pattern, pattern, wrec->w_patternlen);