blob: 6836370bf12359ce049360c16a09fd92c44b878e [file] [log] [blame]
whrb973f2b2000-05-05 19:34:50 +00001/*
2 * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
3 *
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.
7 *
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.
11 *
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.
18 *
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.
22 *
23 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
24 * Mountain View, CA 94043, or:
25 *
26 * http://www.sgi.com
27 *
28 * For further information regarding this notice, see:
29 *
30 * http://oss.sgi.com/projects/GenInfo/NoticeExplan/
31 */
32/*
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.
38 *
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.
44 *
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).
51 *
52 * There is also a function to scan a write logfile in reverse order.
53 *
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.
60 *
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.
64 */
65
66#include <stdio.h>
67#include <unistd.h>
68#include <fcntl.h>
69#include <errno.h>
70#include <string.h>
71#include <sys/param.h>
72#include <sys/stat.h>
73#include <sys/types.h>
74#include "write_log.h"
75
76#ifndef BSIZE
77#ifdef linux
78#define BSIZE DEV_BSIZE
79#else
80#define BSIZE BBSIZE
81#endif
82#endif
83
84#ifndef PATH_MAX
85#define PATH_MAX 255
86/*#define PATH_MAX pathconf("/", _PC_PATH_MAX)*/
87#endif
88
whrb973f2b2000-05-05 19:34:50 +000089char Wlog_Error_String[256];
90
91#if __STDC__
92static int wlog_rec_pack(struct wlog_rec *wrec, char *buf, int flag);
93static int wlog_rec_unpack(struct wlog_rec *wrec, char *buf);
94#else
95static int wlog_rec_pack();
96static int wlog_rec_unpack();
97#endif
98
99/*
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.
103 *
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.
107 *
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
110 * umask.
111 */
112
113int
114wlog_open(wfile, trunc, mode)
115struct wlog_file *wfile;
116int trunc;
117int mode;
118{
119 int omask, oflags;
120
121 if (trunc)
122 trunc = O_TRUNC;
123
124 omask = umask(0);
125
126 /*
127 * Open 1 file descriptor as O_APPEND
128 */
129
130 oflags = O_WRONLY | O_APPEND | O_CREAT | trunc;
131 wfile->w_afd =
132 open(wfile->w_file, oflags, mode);
133 umask(omask);
134
135 if (wfile->w_afd == -1) {
136 sprintf(Wlog_Error_String,
137 "Could not open write_log - open(%s, %#o, %#o) failed: %s\n",
nstraz94181082000-08-30 18:43:38 +0000138 wfile->w_file, oflags, mode, strerror(errno));
whrb973f2b2000-05-05 19:34:50 +0000139 return -1;
140 }
141
142 /*
143 * Open the next fd as a random access descriptor
144 */
145
146 oflags = O_RDWR;
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",
nstraz94181082000-08-30 18:43:38 +0000150 wfile->w_file, oflags, strerror(errno));
whrb973f2b2000-05-05 19:34:50 +0000151 close(wfile->w_afd);
152 wfile->w_afd = -1;
153 return -1;
154 }
155
156 return 0;
157}
158
159/*
160 * Release all resources associated with a wlog_file structure allocated
161 * with the wlog_open() call.
162 */
163
164int
165wlog_close(wfile)
166struct wlog_file *wfile;
167{
168 close(wfile->w_afd);
169 close(wfile->w_rfd);
170 return 0;
171}
172
173/*
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
183 * on-disk record.
184 *
185 * The return value of the function is the byte offset in the logfile
186 * where the record begins.
187 *
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.
192 *
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.
197 */
198
199int
200wlog_record_write(wfile, wrec, offset)
201struct wlog_file *wfile;
202struct wlog_rec *wrec;
203long offset;
204{
205 int reclen;
206 char wbuf[WLOG_REC_MAX_SIZE + 2];
207
208 /*
209 * If offset is -1, we append the record at the end of file
210 *
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.
214 */
215
216 reclen = wlog_rec_pack(wrec, wbuf, (offset < 0));
217
218 if (offset < 0) {
219 /*
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.
223 */
224
225 wbuf[reclen] = reclen / 256;
226 wbuf[reclen+1] = reclen % 256;
227 reclen += 2;
228
229 write(wfile->w_afd, wbuf, reclen);
230 offset = lseek(wfile->w_afd, 0, SEEK_CUR) - reclen;
231 } else {
232 lseek(wfile->w_rfd, offset, SEEK_SET);
233 write(wfile->w_rfd, wbuf, reclen);
234 }
235
236 return offset;
237}
238
239/*
240 * Function to scan a logfile in reverse order. Wfile is a valid
241 * wlog_file structure initialized by wlog_open(). nrecs is the number
242 * of records to scan (all records are scanned if nrecs is 0). func is
243 * a user-supplied function to call for each record found. The function
244 * will be passed a single parameter - a wlog_rec structure .
245 */
246
247int
248wlog_scan_backward(wfile, nrecs, func, data)
249struct wlog_file *wfile;
250int nrecs;
251int (*func)();
252long data;
253{
254 int fd, leftover, nbytes, offset, recnum, reclen, rval;
255 char buf[BSIZE*32], *bufend, *cp, *bufstart;
nstraz94181082000-08-30 18:43:38 +0000256 char albuf[WLOG_REC_MAX_SIZE];
whrb973f2b2000-05-05 19:34:50 +0000257 struct wlog_rec wrec;
258
259 fd = wfile->w_rfd;
260
261 /*
262 * Move to EOF. offset will always hold the current file offset
263 */
264
265 lseek(fd, 0, SEEK_END);
266 offset = lseek(fd, 0, SEEK_CUR);
267
268 bufend = buf + sizeof(buf);
269 bufstart = buf;
270
271 recnum = 0;
272 leftover = 0;
273 while ((!nrecs || recnum < nrecs) && offset > 0) {
274 /*
275 * Check for beginning of file - if there aren't enough bytes
276 * remaining to fill buf, adjust bufstart.
277 */
278
279 if (offset + leftover < sizeof(buf)) {
280 bufstart = bufend - (offset + leftover);
281 offset = 0;
282 } else {
283 offset -= sizeof(buf) - leftover;
284 }
285
286 /*
287 * Move to the proper file offset, and read into buf
288 */
289
290 lseek(fd, offset, SEEK_SET);
291 nbytes = read(fd, bufstart, bufend - bufstart - leftover);
292
293 if (nbytes == -1) {
294 sprintf(Wlog_Error_String,
nstraz94181082000-08-30 18:43:38 +0000295 "Could not read history file at offset %d - read(%d, %#o, %d) failed: %s\n",
296 offset, fd, (int)bufstart,
297 bufend - bufstart - leftover, strerror(errno));
whrb973f2b2000-05-05 19:34:50 +0000298 return -1;
299 }
300
301 cp = bufend;
302 leftover = 0;
303
304 while (cp >= bufstart) {
305
306 /*
307 * If cp-bufstart is not large enough to hold a piece
308 * of record length information, copy remainder to end
309 * of buf and continue reading the file.
310 */
311
312 if (cp - bufstart < 2) {
313 leftover = cp - bufstart;
314 memcpy(bufend - leftover, bufstart, leftover);
315 break;
316 }
317
318 /*
319 * Extract the record length. We must do it this way
320 * instead of casting cp to an int because cp might
321 * not be word aligned.
322 */
323
324 reclen = (*(cp-2) * 256) + *(cp -1);
325
326 /*
327 * If cp-bufstart isn't large enough to hold a
328 * complete record, plus the length information, copy
329 * the leftover bytes to the end of buf and continue
330 * reading.
331 */
332
333 if (cp - bufstart < reclen + 2) {
334 leftover = cp - bufstart;
335 memcpy(bufend - leftover, bufstart, leftover);
336 break;
337 }
338
339 /*
340 * Adjust cp to point at the start of the record.
341 * Copy the record into wbuf so that it is word
342 * aligned and pass the record to the user supplied
343 * function.
344 */
345
346 cp -= reclen + 2;
347 memcpy(albuf, cp, reclen);
348
349 wlog_rec_unpack(&wrec, albuf);
350
351 /*
352 * Call the user supplied function -
353 * stop if instructed to.
354 */
355
356 if ((rval = (*func)(&wrec, data)) == WLOG_STOP_SCAN) {
357 break;
358 }
359
360 recnum++;
361
362 if (nrecs && recnum >= nrecs)
363 break;
364 }
365 }
366
367 return 0;
368}
369
370/*
371 * The following 2 routines are used to pack and unpack the user
372 * visible wlog_rec structure to/from a character buffer which is
373 * stored or read from the write logfile. Any changes to either of
374 * these routines must be reflected in the other.
375 */
376
377static int
378wlog_rec_pack(wrec, buf, flag)
379struct wlog_rec *wrec;
380char *buf;
381int flag;
382{
383 char *file, *host, *pattern;
384 struct wlog_rec_disk *wrecd;
385
386 wrecd = (struct wlog_rec_disk *)buf;
387
388 wrecd->w_pid = (uint)wrec->w_pid;
389 wrecd->w_offset = (uint)wrec->w_offset;
390 wrecd->w_nbytes = (uint)wrec->w_nbytes;
391 wrecd->w_oflags = (uint)wrec->w_oflags;
392 wrecd->w_done = (uint)wrec->w_done;
393 wrecd->w_async = (uint)wrec->w_async;
394
395 wrecd->w_pathlen = (wrec->w_pathlen > 0) ? (uint)wrec->w_pathlen : 0;
396 wrecd->w_hostlen = (wrec->w_hostlen > 0) ? (uint)wrec->w_hostlen : 0;
397 wrecd->w_patternlen = (wrec->w_patternlen > 0) ? (uint)wrec->w_patternlen : 0;
398
399 /*
400 * If flag is true, we should also pack the variable length parts
401 * of the wlog_rec. By default, we only pack the fixed length
402 * parts.
403 */
404
405 if (flag) {
406 file = buf + sizeof(struct wlog_rec_disk);
407 host = file + wrecd->w_pathlen;
408 pattern = host + wrecd->w_hostlen;
409
410 if (wrecd->w_pathlen > 0)
411 memcpy(file, wrec->w_path, wrecd->w_pathlen);
412
413 if (wrecd->w_hostlen > 0)
414 memcpy(host, wrec->w_host, wrecd->w_hostlen);
415
416 if (wrecd->w_patternlen > 0)
417 memcpy(pattern, wrec->w_pattern, wrecd->w_patternlen);
418
419 return (sizeof(struct wlog_rec_disk) +
420 wrecd->w_pathlen + wrecd->w_hostlen + wrecd->w_patternlen);
421 } else {
422 return sizeof(struct wlog_rec_disk);
423 }
424}
425
426static int
427wlog_rec_unpack(wrec, buf)
428struct wlog_rec *wrec;
429char *buf;
430{
431 char *file, *host, *pattern;
432 struct wlog_rec_disk *wrecd;
433
434 bzero((char *)wrec, sizeof(struct wlog_rec));
435 wrecd = (struct wlog_rec_disk *)buf;
436
437 wrec->w_pid = wrecd->w_pid;
438 wrec->w_offset = wrecd->w_offset;
439 wrec->w_nbytes = wrecd->w_nbytes;
440 wrec->w_oflags = wrecd->w_oflags;
441 wrec->w_hostlen = wrecd->w_hostlen;
442 wrec->w_pathlen = wrecd->w_pathlen;
443 wrec->w_patternlen = wrecd->w_patternlen;
444 wrec->w_done = wrecd->w_done;
445 wrec->w_async = wrecd->w_async;
446
447 if (wrec->w_pathlen > 0) {
448 file = buf + sizeof(struct wlog_rec_disk);
449 memcpy(wrec->w_path, file, wrec->w_pathlen);
450 }
451
452 if (wrec->w_hostlen > 0) {
453 host = buf + sizeof(struct wlog_rec_disk) + wrec->w_pathlen;
454 memcpy(wrec->w_host, host, wrec->w_hostlen);
455 }
456
457 if (wrec->w_patternlen > 0) {
458 pattern = buf + sizeof(struct wlog_rec_disk) +
459 wrec->w_pathlen + wrec->w_hostlen;
460 memcpy(wrec->w_pattern, pattern, wrec->w_patternlen);
461 }
462
463 return 0;
464}