blob: 1731dc3203467ec98371ec7244462bb30cddcf62 [file] [log] [blame]
whrb973f2b2000-05-05 19:34:50 +00001/*
2 * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved.
vapier45a8ba02009-07-20 10:59:32 +00003 *
whrb973f2b2000-05-05 19:34:50 +00004 * 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.
vapier45a8ba02009-07-20 10:59:32 +00007 *
whrb973f2b2000-05-05 19:34:50 +00008 * 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.
vapier45a8ba02009-07-20 10:59:32 +000011 *
whrb973f2b2000-05-05 19:34:50 +000012 * 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.
vapier45a8ba02009-07-20 10:59:32 +000018 *
whrb973f2b2000-05-05 19:34:50 +000019 * You should have received a copy of the GNU General Public License along
Wanlong Gaofed96412012-10-24 10:10:29 +080020 * with this program; if not, write the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
vapier45a8ba02009-07-20 10:59:32 +000022 *
whrb973f2b2000-05-05 19:34:50 +000023 * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
24 * Mountain View, CA 94043, or:
vapier45a8ba02009-07-20 10:59:32 +000025 *
26 * http://www.sgi.com
27 *
28 * For further information regarding this notice, see:
29 *
whrb973f2b2000-05-05 19:34:50 +000030 * 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
vapier45a8ba02009-07-20 10:59:32 +000048 * on whether or not the write has been confirmed as complete. This
whrb973f2b2000-05-05 19:34:50 +000049 * 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
mridgee6508f82005-01-04 21:00:17 +000077#ifdef DEV_BSIZE
whrb973f2b2000-05-05 19:34:50 +000078#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
Wanlong Gao354ebb42012-12-07 10:10:04 +080089char Wlog_Error_String[256];
whrb973f2b2000-05-05 19:34:50 +000090
91#if __STDC__
Wanlong Gao354ebb42012-12-07 10:10:04 +080092static int wlog_rec_pack(struct wlog_rec *wrec, char *buf, int flag);
93static int wlog_rec_unpack(struct wlog_rec *wrec, char *buf);
whrb973f2b2000-05-05 19:34:50 +000094#else
Wanlong Gao354ebb42012-12-07 10:10:04 +080095static int wlog_rec_pack();
96static int wlog_rec_unpack();
whrb973f2b2000-05-05 19:34:50 +000097#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
Wanlong Gao354ebb42012-12-07 10:10:04 +0800113int wlog_open(wfile, trunc, mode)
114struct wlog_file *wfile;
115int trunc;
116int mode;
whrb973f2b2000-05-05 19:34:50 +0000117{
Wanlong Gao354ebb42012-12-07 10:10:04 +0800118 int omask, oflags;
whrb973f2b2000-05-05 19:34:50 +0000119
120 if (trunc)
121 trunc = O_TRUNC;
122
123 omask = umask(0);
124
125 /*
126 * Open 1 file descriptor as O_APPEND
127 */
128
129 oflags = O_WRONLY | O_APPEND | O_CREAT | trunc;
Wanlong Gao354ebb42012-12-07 10:10:04 +0800130 wfile->w_afd = open(wfile->w_file, oflags, mode);
whrb973f2b2000-05-05 19:34:50 +0000131 umask(omask);
132
133 if (wfile->w_afd == -1) {
134 sprintf(Wlog_Error_String,
135 "Could not open write_log - open(%s, %#o, %#o) failed: %s\n",
nstraz94181082000-08-30 18:43:38 +0000136 wfile->w_file, oflags, mode, strerror(errno));
whrb973f2b2000-05-05 19:34:50 +0000137 return -1;
138 }
139
140 /*
141 * Open the next fd as a random access descriptor
142 */
143
144 oflags = O_RDWR;
subrata_modake753fd72008-07-11 10:56:34 +0000145 if ((wfile->w_rfd = open(wfile->w_file, oflags)) == -1) {
whrb973f2b2000-05-05 19:34:50 +0000146 sprintf(Wlog_Error_String,
147 "Could not open write log - open(%s, %#o) failed: %s\n",
nstraz94181082000-08-30 18:43:38 +0000148 wfile->w_file, oflags, strerror(errno));
whrb973f2b2000-05-05 19:34:50 +0000149 close(wfile->w_afd);
150 wfile->w_afd = -1;
151 return -1;
152 }
vapier45a8ba02009-07-20 10:59:32 +0000153
whrb973f2b2000-05-05 19:34:50 +0000154 return 0;
155}
156
157/*
158 * Release all resources associated with a wlog_file structure allocated
159 * with the wlog_open() call.
160 */
161
Wanlong Gao354ebb42012-12-07 10:10:04 +0800162int wlog_close(wfile)
163struct wlog_file *wfile;
whrb973f2b2000-05-05 19:34:50 +0000164{
165 close(wfile->w_afd);
166 close(wfile->w_rfd);
167 return 0;
168}
169
170/*
171 * Write a wlog_rec structure to a write logfile. Offset is used to
172 * control where the record will be written. If offset is < 0, the
173 * record will be appended to the end of the logfile. Otherwise, the
174 * record which exists at the indicated offset will be overlayed. This
175 * is so that we can record writes which are outstanding (with the w_done
176 * bit in wrec cleared), but not completed, and then later update the
177 * logfile when the write request completes (as with async io). When
vapier45a8ba02009-07-20 10:59:32 +0000178 * offset is >= 0, only the fixed length portion of the record is
whrb973f2b2000-05-05 19:34:50 +0000179 * rewritten. See text in write_log.h for details on the format of an
180 * on-disk record.
vapier45a8ba02009-07-20 10:59:32 +0000181 *
whrb973f2b2000-05-05 19:34:50 +0000182 * The return value of the function is the byte offset in the logfile
183 * where the record begins.
184 *
185 * Note: It is the callers responsibility to make sure that the offset
186 * parameter 'points' to a valid record location when a record is to be
187 * overlayed. This is guarenteed by saving the return value of a previous
188 * call to wlog_record_write() which wrote the record to be overlayed.
189 *
190 * Note2: The on-disk version of the wlog_rec is MUCH different than
191 * the user version. Don't expect to od the logfile and see data formatted
192 * as it is in the wlog_rec structure. Considerable data packing takes
193 * place before the record is written.
194 */
195
Wanlong Gao354ebb42012-12-07 10:10:04 +0800196int wlog_record_write(wfile, wrec, offset)
197struct wlog_file *wfile;
198struct wlog_rec *wrec;
199long offset;
whrb973f2b2000-05-05 19:34:50 +0000200{
Wanlong Gao354ebb42012-12-07 10:10:04 +0800201 int reclen;
202 char wbuf[WLOG_REC_MAX_SIZE + 2];
whrb973f2b2000-05-05 19:34:50 +0000203
whrb973f2b2000-05-05 19:34:50 +0000204 /*
Wanlong Gao354ebb42012-12-07 10:10:04 +0800205 * If offset is -1, we append the record at the end of file
206 *
207 * Otherwise, we overlay wrec at the file offset indicated and assume
208 * that the caller passed us the correct offset. We do not record the
209 * fname in this case.
whrb973f2b2000-05-05 19:34:50 +0000210 */
vapier45a8ba02009-07-20 10:59:32 +0000211
Wanlong Gao354ebb42012-12-07 10:10:04 +0800212 reclen = wlog_rec_pack(wrec, wbuf, (offset < 0));
whrb973f2b2000-05-05 19:34:50 +0000213
Wanlong Gao354ebb42012-12-07 10:10:04 +0800214 if (offset < 0) {
215 /*
216 * Since we're writing a complete new record, we must also tack
217 * its length onto the end so that wlog_scan_backward() will work.
218 * Length is asumed to fit into 2 bytes.
219 */
vapier45a8ba02009-07-20 10:59:32 +0000220
Wanlong Gao354ebb42012-12-07 10:10:04 +0800221 wbuf[reclen] = reclen / 256;
222 wbuf[reclen + 1] = reclen % 256;
223 reclen += 2;
224
225 if (write(wfile->w_afd, wbuf, reclen) == -1) {
226 sprintf(Wlog_Error_String,
227 "Could not write log - write(%s, %s, %d) failed: %s\n",
228 wfile->w_file, wbuf, reclen, strerror(errno));
229 return -1;
230 } else {
231 offset = lseek(wfile->w_afd, 0, SEEK_CUR) - reclen;
232 if (offset == -1) {
233 sprintf(Wlog_Error_String,
234 "Could not reposition file pointer - lseek(%s, 0, SEEK_CUR) failed: %s\n",
235 wfile->w_file, strerror(errno));
236 return -1;
237 }
238 }
239 } else {
240 if ((lseek(wfile->w_rfd, offset, SEEK_SET)) == -1) {
241 sprintf(Wlog_Error_String,
242 "Could not reposition file pointer - lseek(%s, %ld, SEEK_SET) failed: %s\n",
243 wfile->w_file, offset, strerror(errno));
244 return -1;
245 } else {
246 if ((write(wfile->w_rfd, wbuf, reclen)) == -1) {
247 sprintf(Wlog_Error_String,
248 "Could not write log - write(%s, %s, %d) failed: %s\n",
249 wfile->w_file, wbuf, reclen,
250 strerror(errno));
251 return -1;
252 }
253 }
254 }
255
256 return offset;
whrb973f2b2000-05-05 19:34:50 +0000257}
258
259/*
260 * Function to scan a logfile in reverse order. Wfile is a valid
261 * wlog_file structure initialized by wlog_open(). nrecs is the number
262 * of records to scan (all records are scanned if nrecs is 0). func is
263 * a user-supplied function to call for each record found. The function
264 * will be passed a single parameter - a wlog_rec structure .
265 */
266
Wanlong Gao354ebb42012-12-07 10:10:04 +0800267int wlog_scan_backward(wfile, nrecs, func, data)
268struct wlog_file *wfile;
269int nrecs;
270int (*func) ();
271long data;
whrb973f2b2000-05-05 19:34:50 +0000272{
Wanlong Gao354ebb42012-12-07 10:10:04 +0800273 int fd, leftover, nbytes, offset, recnum, reclen, rval;
274 char buf[BSIZE * 32], *bufend, *cp, *bufstart;
275 char albuf[WLOG_REC_MAX_SIZE];
276 struct wlog_rec wrec;
whrb973f2b2000-05-05 19:34:50 +0000277
278 fd = wfile->w_rfd;
279
280 /*
281 * Move to EOF. offset will always hold the current file offset
282 */
283
Wanlong Gao354ebb42012-12-07 10:10:04 +0800284 if ((lseek(fd, 0, SEEK_END)) == -1) {
285 sprintf(Wlog_Error_String,
286 "Could not reposition file pointer - lseek(%s, 0, SEEK_END) failed: %s\n",
287 wfile->w_file, strerror(errno));
288 return -1;
289 }
whrb973f2b2000-05-05 19:34:50 +0000290 offset = lseek(fd, 0, SEEK_CUR);
Wanlong Gao354ebb42012-12-07 10:10:04 +0800291 if ((offset == -1)) {
292 sprintf(Wlog_Error_String,
293 "Could not reposition file pointer - lseek(%s, 0, SEEK_CUR) failed: %s\n",
294 wfile->w_file, strerror(errno));
295 return -1;
296 }
whrb973f2b2000-05-05 19:34:50 +0000297
298 bufend = buf + sizeof(buf);
299 bufstart = buf;
300
301 recnum = 0;
302 leftover = 0;
303 while ((!nrecs || recnum < nrecs) && offset > 0) {
304 /*
305 * Check for beginning of file - if there aren't enough bytes
306 * remaining to fill buf, adjust bufstart.
307 */
308
robbiew0ccc8272003-06-18 17:44:03 +0000309 if ((unsigned int)offset + leftover < sizeof(buf)) {
whrb973f2b2000-05-05 19:34:50 +0000310 bufstart = bufend - (offset + leftover);
311 offset = 0;
312 } else {
313 offset -= sizeof(buf) - leftover;
314 }
315
vapier45a8ba02009-07-20 10:59:32 +0000316 /*
whrb973f2b2000-05-05 19:34:50 +0000317 * Move to the proper file offset, and read into buf
318 */
Wanlong Gao354ebb42012-12-07 10:10:04 +0800319 if ((lseek(fd, offset, SEEK_SET)) == -1) {
320 sprintf(Wlog_Error_String,
321 "Could not reposition file pointer - lseek(%s, %d, SEEK_SET) failed: %s\n",
322 wfile->w_file, offset, strerror(errno));
323 return -1;
324 }
whrb973f2b2000-05-05 19:34:50 +0000325
whrb973f2b2000-05-05 19:34:50 +0000326 nbytes = read(fd, bufstart, bufend - bufstart - leftover);
327
328 if (nbytes == -1) {
329 sprintf(Wlog_Error_String,
robbiewf3a83d52002-05-28 16:26:16 +0000330 "Could not read history file at offset %d - read(%d, %p, %d) failed: %s\n",
robbiew15226cd2002-04-10 16:10:40 +0000331 offset, fd, bufstart,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800332 (int)(bufend - bufstart - leftover),
333 strerror(errno));
whrb973f2b2000-05-05 19:34:50 +0000334 return -1;
335 }
336
337 cp = bufend;
338 leftover = 0;
339
340 while (cp >= bufstart) {
341
342 /*
343 * If cp-bufstart is not large enough to hold a piece
344 * of record length information, copy remainder to end
345 * of buf and continue reading the file.
346 */
347
348 if (cp - bufstart < 2) {
349 leftover = cp - bufstart;
350 memcpy(bufend - leftover, bufstart, leftover);
351 break;
352 }
353
354 /*
355 * Extract the record length. We must do it this way
356 * instead of casting cp to an int because cp might
357 * not be word aligned.
358 */
359
Wanlong Gao354ebb42012-12-07 10:10:04 +0800360 reclen = (*(cp - 2) * 256) + *(cp - 1);
whrb973f2b2000-05-05 19:34:50 +0000361
362 /*
363 * If cp-bufstart isn't large enough to hold a
364 * complete record, plus the length information, copy
365 * the leftover bytes to the end of buf and continue
366 * reading.
367 */
368
369 if (cp - bufstart < reclen + 2) {
370 leftover = cp - bufstart;
371 memcpy(bufend - leftover, bufstart, leftover);
372 break;
373 }
374
375 /*
376 * Adjust cp to point at the start of the record.
377 * Copy the record into wbuf so that it is word
378 * aligned and pass the record to the user supplied
379 * function.
380 */
381
382 cp -= reclen + 2;
383 memcpy(albuf, cp, reclen);
384
385 wlog_rec_unpack(&wrec, albuf);
386
387 /*
388 * Call the user supplied function -
389 * stop if instructed to.
390 */
391
Wanlong Gao354ebb42012-12-07 10:10:04 +0800392 if ((rval = (*func) (&wrec, data)) == WLOG_STOP_SCAN) {
whrb973f2b2000-05-05 19:34:50 +0000393 break;
394 }
395
396 recnum++;
397
398 if (nrecs && recnum >= nrecs)
399 break;
400 }
401 }
402
403 return 0;
404}
405
406/*
407 * The following 2 routines are used to pack and unpack the user
408 * visible wlog_rec structure to/from a character buffer which is
409 * stored or read from the write logfile. Any changes to either of
410 * these routines must be reflected in the other.
411 */
412
Wanlong Gao354ebb42012-12-07 10:10:04 +0800413static int wlog_rec_pack(wrec, buf, flag)
414struct wlog_rec *wrec;
415char *buf;
416int flag;
whrb973f2b2000-05-05 19:34:50 +0000417{
Wanlong Gao354ebb42012-12-07 10:10:04 +0800418 char *file, *host, *pattern;
419 struct wlog_rec_disk *wrecd;
whrb973f2b2000-05-05 19:34:50 +0000420
421 wrecd = (struct wlog_rec_disk *)buf;
422
Wanlong Gao354ebb42012-12-07 10:10:04 +0800423 wrecd->w_pid = (uint) wrec->w_pid;
424 wrecd->w_offset = (uint) wrec->w_offset;
425 wrecd->w_nbytes = (uint) wrec->w_nbytes;
426 wrecd->w_oflags = (uint) wrec->w_oflags;
427 wrecd->w_done = (uint) wrec->w_done;
428 wrecd->w_async = (uint) wrec->w_async;
whrb973f2b2000-05-05 19:34:50 +0000429
Wanlong Gao354ebb42012-12-07 10:10:04 +0800430 wrecd->w_pathlen = (wrec->w_pathlen > 0) ? (uint) wrec->w_pathlen : 0;
431 wrecd->w_hostlen = (wrec->w_hostlen > 0) ? (uint) wrec->w_hostlen : 0;
432 wrecd->w_patternlen =
433 (wrec->w_patternlen > 0) ? (uint) wrec->w_patternlen : 0;
whrb973f2b2000-05-05 19:34:50 +0000434
435 /*
436 * If flag is true, we should also pack the variable length parts
437 * of the wlog_rec. By default, we only pack the fixed length
438 * parts.
439 */
440
441 if (flag) {
442 file = buf + sizeof(struct wlog_rec_disk);
443 host = file + wrecd->w_pathlen;
444 pattern = host + wrecd->w_hostlen;
vapier45a8ba02009-07-20 10:59:32 +0000445
whrb973f2b2000-05-05 19:34:50 +0000446 if (wrecd->w_pathlen > 0)
447 memcpy(file, wrec->w_path, wrecd->w_pathlen);
vapier45a8ba02009-07-20 10:59:32 +0000448
whrb973f2b2000-05-05 19:34:50 +0000449 if (wrecd->w_hostlen > 0)
450 memcpy(host, wrec->w_host, wrecd->w_hostlen);
vapier45a8ba02009-07-20 10:59:32 +0000451
whrb973f2b2000-05-05 19:34:50 +0000452 if (wrecd->w_patternlen > 0)
453 memcpy(pattern, wrec->w_pattern, wrecd->w_patternlen);
454
455 return (sizeof(struct wlog_rec_disk) +
Wanlong Gao354ebb42012-12-07 10:10:04 +0800456 wrecd->w_pathlen + wrecd->w_hostlen +
457 wrecd->w_patternlen);
whrb973f2b2000-05-05 19:34:50 +0000458 } else {
459 return sizeof(struct wlog_rec_disk);
460 }
461}
462
Wanlong Gao354ebb42012-12-07 10:10:04 +0800463static int wlog_rec_unpack(wrec, buf)
464struct wlog_rec *wrec;
465char *buf;
whrb973f2b2000-05-05 19:34:50 +0000466{
Wanlong Gao354ebb42012-12-07 10:10:04 +0800467 char *file, *host, *pattern;
468 struct wlog_rec_disk *wrecd;
whrb973f2b2000-05-05 19:34:50 +0000469
vapier32cc0ac2006-06-22 04:28:32 +0000470 memset((char *)wrec, 0x00, sizeof(struct wlog_rec));
whrb973f2b2000-05-05 19:34:50 +0000471 wrecd = (struct wlog_rec_disk *)buf;
472
473 wrec->w_pid = wrecd->w_pid;
474 wrec->w_offset = wrecd->w_offset;
475 wrec->w_nbytes = wrecd->w_nbytes;
476 wrec->w_oflags = wrecd->w_oflags;
477 wrec->w_hostlen = wrecd->w_hostlen;
478 wrec->w_pathlen = wrecd->w_pathlen;
479 wrec->w_patternlen = wrecd->w_patternlen;
480 wrec->w_done = wrecd->w_done;
481 wrec->w_async = wrecd->w_async;
482
483 if (wrec->w_pathlen > 0) {
484 file = buf + sizeof(struct wlog_rec_disk);
485 memcpy(wrec->w_path, file, wrec->w_pathlen);
486 }
487
488 if (wrec->w_hostlen > 0) {
489 host = buf + sizeof(struct wlog_rec_disk) + wrec->w_pathlen;
490 memcpy(wrec->w_host, host, wrec->w_hostlen);
491 }
492
493 if (wrec->w_patternlen > 0) {
494 pattern = buf + sizeof(struct wlog_rec_disk) +
Wanlong Gao354ebb42012-12-07 10:10:04 +0800495 wrec->w_pathlen + wrec->w_hostlen;
whrb973f2b2000-05-05 19:34:50 +0000496 memcpy(wrec->w_pattern, pattern, wrec->w_patternlen);
497 }
498
499 return 0;
Chris Dearmanec6edca2012-10-17 19:54:01 -0700500}