2 * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of version 2 of the GNU General Public License as
6 * published by the Free Software Foundation.
8 * This program is distributed in the hope that it would be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 * Further, this software is distributed without any warranty that it is
13 * free of the rightful claim of any third person regarding infringement
14 * or the like. Any license provided herein, whether implied or
15 * otherwise, applies only to this software file. Patent licenses, if
16 * any, provided herein do not apply to combinations of this program with
17 * other software, or any other product whatsoever.
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write the Free Software Foundation, Inc., 59
21 * Temple Place - Suite 330, Boston MA 02111-1307, USA.
23 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
24 * Mountain View, CA 94043, or:
28 * For further information regarding this notice, see:
30 * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
33 * This module contains code for logging writes to files, and for
34 * perusing the resultant logfile. The main intent of all this is
35 * to provide a 'write history' of a file which can be examined to
36 * judge the state of a file (ie. whether it is corrupted or not) based
37 * on the write activity.
39 * The main abstractions available to the user are the wlog_file, and
40 * the wlog_rec. A wlog_file is a handle encapsulating a write logfile.
41 * It is initialized with the wlog_open() function. This handle is
42 * then passed to the various wlog_xxx() functions to provide transparent
43 * access to the write logfile.
45 * The wlog_rec datatype is a structure which contains all the information
46 * about a file write. Examples include the file name, offset, length,
47 * pattern, etc. In addition there is a bit which is cleared/set based
48 * on whether or not the write has been confirmed as complete. This
49 * allows the write logfile to contain information on writes which have
50 * been initiated, but not yet completed (as in async io).
52 * There is also a function to scan a write logfile in reverse order.
54 * NOTE: For target file analysis based on a write logfile, the
55 * assumption is made that the file being written to is
56 * locked from simultaneous access, so that the order of
57 * write completion is predictable. This is an issue when
58 * more than 1 process is trying to write data to the same
59 * target file simultaneously.
61 * The history file created is a collection of variable length records
62 * described by scruct wlog_rec_disk in write_log.h. See that module for
63 * the layout of the data on disk.
71 #include <sys/param.h>
73 #include <sys/types.h>
74 #include "write_log.h"
78 #define BSIZE DEV_BSIZE
86 /*#define PATH_MAX pathconf("/", _PC_PATH_MAX)*/
89 char Wlog_Error_String
[256];
92 static int wlog_rec_pack(struct wlog_rec
*wrec
, char *buf
, int flag
);
93 static int wlog_rec_unpack(struct wlog_rec
*wrec
, char *buf
);
95 static int wlog_rec_pack();
96 static int wlog_rec_unpack();
100 * Initialize a write logfile. wfile is a wlog_file structure that has
101 * the w_file field filled in. The rest of the information in the
102 * structure is initialized by the routine.
104 * The trunc flag is used to indicate whether or not the logfile should
105 * be truncated if it currently exists. If it is non-zero, the file will
106 * be truncated, otherwise it will be appended to.
108 * The mode argument is the [absolute] mode which the file will be
109 * given if it does not exist. This mode is not affected by your process
114 wlog_open(wfile
, trunc
, mode
)
115 struct wlog_file
*wfile
;
127 * Open 1 file descriptor as O_APPEND
130 oflags
= O_WRONLY
| O_APPEND
| O_CREAT
| trunc
;
132 open(wfile
->w_file
, oflags
, mode
);
135 if (wfile
->w_afd
== -1) {
136 sprintf(Wlog_Error_String
,
137 "Could not open write_log - open(%s, %#o, %#o) failed: %s\n",
138 wfile
->w_file
, oflags
, mode
, strerror(errno
));
143 * Open the next fd as a random access descriptor
147 if ((wfile
->w_rfd
= open(wfile
->w_file
, oflags
)) == -1) {
148 sprintf(Wlog_Error_String
,
149 "Could not open write log - open(%s, %#o) failed: %s\n",
150 wfile
->w_file
, oflags
, strerror(errno
));
160 * Release all resources associated with a wlog_file structure allocated
161 * with the wlog_open() call.
166 struct wlog_file
*wfile
;
174 * Write a wlog_rec structure to a write logfile. Offset is used to
175 * control where the record will be written. If offset is < 0, the
176 * record will be appended to the end of the logfile. Otherwise, the
177 * record which exists at the indicated offset will be overlayed. This
178 * is so that we can record writes which are outstanding (with the w_done
179 * bit in wrec cleared), but not completed, and then later update the
180 * logfile when the write request completes (as with async io). When
181 * offset is >= 0, only the fixed length portion of the record is
182 * rewritten. See text in write_log.h for details on the format of an
185 * The return value of the function is the byte offset in the logfile
186 * where the record begins.
188 * Note: It is the callers responsibility to make sure that the offset
189 * parameter 'points' to a valid record location when a record is to be
190 * overlayed. This is guarenteed by saving the return value of a previous
191 * call to wlog_record_write() which wrote the record to be overlayed.
193 * Note2: The on-disk version of the wlog_rec is MUCH different than
194 * the user version. Don't expect to od the logfile and see data formatted
195 * as it is in the wlog_rec structure. Considerable data packing takes
196 * place before the record is written.
200 wlog_record_write(wfile
, wrec
, offset
)
201 struct wlog_file
*wfile
;
202 struct wlog_rec
*wrec
;
206 char wbuf
[WLOG_REC_MAX_SIZE
+ 2];
209 * If offset is -1, we append the record at the end of file
211 * Otherwise, we overlay wrec at the file offset indicated and assume
212 * that the caller passed us the correct offset. We do not record the
213 * fname in this case.
216 reclen
= wlog_rec_pack(wrec
, wbuf
, (offset
< 0));
220 * Since we're writing a complete new record, we must also tack
221 * its length onto the end so that wlog_scan_backward() will work.
222 * Length is asumed to fit into 2 bytes.
225 wbuf
[reclen
] = reclen
/ 256;
226 wbuf
[reclen
+1] = reclen
% 256;
229 if ( write(wfile
->w_afd
, wbuf
, reclen
) == -1 ) {
230 sprintf(Wlog_Error_String
,
231 "Could not write log - write(%s, %s, %d) failed: %s\n",
232 wfile
->w_file
, wbuf
, reclen
, strerror(errno
));
235 offset
= lseek(wfile
->w_afd
, 0, SEEK_CUR
) - reclen
;
236 if ( offset
== -1 ) {
237 sprintf(Wlog_Error_String
,
238 "Could not reposition file pointer - lseek(%s, 0, SEEK_CUR) failed: %s\n",
239 wfile
->w_file
, strerror(errno
));
244 if ( (lseek(wfile
->w_rfd
, offset
, SEEK_SET
)) == -1 ) {
245 sprintf(Wlog_Error_String
,
246 "Could not reposition file pointer - lseek(%s, %ld, SEEK_SET) failed: %s\n",
247 wfile
->w_file
, offset
, strerror(errno
));
250 if ( (write(wfile
->w_rfd
, wbuf
, reclen
)) == -1 ) {
251 sprintf(Wlog_Error_String
,
252 "Could not write log - write(%s, %s, %d) failed: %s\n",
253 wfile
->w_file
, wbuf
, reclen
, strerror(errno
));
263 * Function to scan a logfile in reverse order. Wfile is a valid
264 * wlog_file structure initialized by wlog_open(). nrecs is the number
265 * of records to scan (all records are scanned if nrecs is 0). func is
266 * a user-supplied function to call for each record found. The function
267 * will be passed a single parameter - a wlog_rec structure .
271 wlog_scan_backward(wfile
, nrecs
, func
, data
)
272 struct wlog_file
*wfile
;
277 int fd
, leftover
, nbytes
, offset
, recnum
, reclen
, rval
;
278 char buf
[BSIZE
*32], *bufend
, *cp
, *bufstart
;
279 char albuf
[WLOG_REC_MAX_SIZE
];
280 struct wlog_rec wrec
;
285 * Move to EOF. offset will always hold the current file offset
288 if ( (lseek(fd
, 0, SEEK_END
)) == -1 ) {
289 sprintf(Wlog_Error_String
,
290 "Could not reposition file pointer - lseek(%s, 0, SEEK_END) failed: %s\n",
291 wfile
->w_file
, strerror(errno
));
294 offset
= lseek(fd
, 0, SEEK_CUR
);
295 if ( (offset
== -1) ) {
296 sprintf(Wlog_Error_String
,
297 "Could not reposition file pointer - lseek(%s, 0, SEEK_CUR) failed: %s\n",
298 wfile
->w_file
, strerror(errno
));
302 bufend
= buf
+ sizeof(buf
);
307 while ((!nrecs
|| recnum
< nrecs
) && offset
> 0) {
309 * Check for beginning of file - if there aren't enough bytes
310 * remaining to fill buf, adjust bufstart.
313 if ((unsigned int)offset
+ leftover
< sizeof(buf
)) {
314 bufstart
= bufend
- (offset
+ leftover
);
317 offset
-= sizeof(buf
) - leftover
;
321 * Move to the proper file offset, and read into buf
323 if ( (lseek(fd
, offset
, SEEK_SET
)) ==-1 ) {
324 sprintf(Wlog_Error_String
,
325 "Could not reposition file pointer - lseek(%s, %d, SEEK_SET) failed: %s\n",
326 wfile
->w_file
, offset
, strerror(errno
));
330 nbytes
= read(fd
, bufstart
, bufend
- bufstart
- leftover
);
333 sprintf(Wlog_Error_String
,
334 "Could not read history file at offset %d - read(%d, %p, %d) failed: %s\n",
335 offset
, fd
, bufstart
,
336 (int)(bufend
- bufstart
- leftover
), strerror(errno
));
343 while (cp
>= bufstart
) {
346 * If cp-bufstart is not large enough to hold a piece
347 * of record length information, copy remainder to end
348 * of buf and continue reading the file.
351 if (cp
- bufstart
< 2) {
352 leftover
= cp
- bufstart
;
353 memcpy(bufend
- leftover
, bufstart
, leftover
);
358 * Extract the record length. We must do it this way
359 * instead of casting cp to an int because cp might
360 * not be word aligned.
363 reclen
= (*(cp
-2) * 256) + *(cp
-1);
366 * If cp-bufstart isn't large enough to hold a
367 * complete record, plus the length information, copy
368 * the leftover bytes to the end of buf and continue
372 if (cp
- bufstart
< reclen
+ 2) {
373 leftover
= cp
- bufstart
;
374 memcpy(bufend
- leftover
, bufstart
, leftover
);
379 * Adjust cp to point at the start of the record.
380 * Copy the record into wbuf so that it is word
381 * aligned and pass the record to the user supplied
386 memcpy(albuf
, cp
, reclen
);
388 wlog_rec_unpack(&wrec
, albuf
);
391 * Call the user supplied function -
392 * stop if instructed to.
395 if ((rval
= (*func
)(&wrec
, data
)) == WLOG_STOP_SCAN
) {
401 if (nrecs
&& recnum
>= nrecs
)
410 * The following 2 routines are used to pack and unpack the user
411 * visible wlog_rec structure to/from a character buffer which is
412 * stored or read from the write logfile. Any changes to either of
413 * these routines must be reflected in the other.
417 wlog_rec_pack(wrec
, buf
, flag
)
418 struct wlog_rec
*wrec
;
422 char *file
, *host
, *pattern
;
423 struct wlog_rec_disk
*wrecd
;
425 wrecd
= (struct wlog_rec_disk
*)buf
;
427 wrecd
->w_pid
= (uint
)wrec
->w_pid
;
428 wrecd
->w_offset
= (uint
)wrec
->w_offset
;
429 wrecd
->w_nbytes
= (uint
)wrec
->w_nbytes
;
430 wrecd
->w_oflags
= (uint
)wrec
->w_oflags
;
431 wrecd
->w_done
= (uint
)wrec
->w_done
;
432 wrecd
->w_async
= (uint
)wrec
->w_async
;
434 wrecd
->w_pathlen
= (wrec
->w_pathlen
> 0) ? (uint
)wrec
->w_pathlen
: 0;
435 wrecd
->w_hostlen
= (wrec
->w_hostlen
> 0) ? (uint
)wrec
->w_hostlen
: 0;
436 wrecd
->w_patternlen
= (wrec
->w_patternlen
> 0) ? (uint
)wrec
->w_patternlen
: 0;
439 * If flag is true, we should also pack the variable length parts
440 * of the wlog_rec. By default, we only pack the fixed length
445 file
= buf
+ sizeof(struct wlog_rec_disk
);
446 host
= file
+ wrecd
->w_pathlen
;
447 pattern
= host
+ wrecd
->w_hostlen
;
449 if (wrecd
->w_pathlen
> 0)
450 memcpy(file
, wrec
->w_path
, wrecd
->w_pathlen
);
452 if (wrecd
->w_hostlen
> 0)
453 memcpy(host
, wrec
->w_host
, wrecd
->w_hostlen
);
455 if (wrecd
->w_patternlen
> 0)
456 memcpy(pattern
, wrec
->w_pattern
, wrecd
->w_patternlen
);
458 return (sizeof(struct wlog_rec_disk
) +
459 wrecd
->w_pathlen
+ wrecd
->w_hostlen
+ wrecd
->w_patternlen
);
461 return sizeof(struct wlog_rec_disk
);
466 wlog_rec_unpack(wrec
, buf
)
467 struct wlog_rec
*wrec
;
470 char *file
, *host
, *pattern
;
471 struct wlog_rec_disk
*wrecd
;
473 memset((char *)wrec
, 0x00, sizeof(struct wlog_rec
));
474 wrecd
= (struct wlog_rec_disk
*)buf
;
476 wrec
->w_pid
= wrecd
->w_pid
;
477 wrec
->w_offset
= wrecd
->w_offset
;
478 wrec
->w_nbytes
= wrecd
->w_nbytes
;
479 wrec
->w_oflags
= wrecd
->w_oflags
;
480 wrec
->w_hostlen
= wrecd
->w_hostlen
;
481 wrec
->w_pathlen
= wrecd
->w_pathlen
;
482 wrec
->w_patternlen
= wrecd
->w_patternlen
;
483 wrec
->w_done
= wrecd
->w_done
;
484 wrec
->w_async
= wrecd
->w_async
;
486 if (wrec
->w_pathlen
> 0) {
487 file
= buf
+ sizeof(struct wlog_rec_disk
);
488 memcpy(wrec
->w_path
, file
, wrec
->w_pathlen
);
491 if (wrec
->w_hostlen
> 0) {
492 host
= buf
+ sizeof(struct wlog_rec_disk
) + wrec
->w_pathlen
;
493 memcpy(wrec
->w_host
, host
, wrec
->w_hostlen
);
496 if (wrec
->w_patternlen
> 0) {
497 pattern
= buf
+ sizeof(struct wlog_rec_disk
) +
498 wrec
->w_pathlen
+ wrec
->w_hostlen
;
499 memcpy(wrec
->w_pattern
, pattern
, wrec
->w_patternlen
);