d6c81a77d58a86f9bc06ddc55da62657940cc2dc
[xfstests-dev.git] / lib / write_log.c
1 /*
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 <strings.h>
72 #include <sys/param.h>
73 #include <sys/stat.h>
74 #include <sys/types.h>
75 #include "write_log.h"
76
77 #ifndef BSIZE
78 #ifdef linux
79 #define BSIZE DEV_BSIZE
80 #else
81 #define BSIZE BBSIZE
82 #endif
83 #endif
84
85 #ifndef PATH_MAX
86 #define PATH_MAX          255
87 /*#define PATH_MAX pathconf("/", _PC_PATH_MAX)*/
88 #endif
89
90 char    Wlog_Error_String[256];
91
92 #if __STDC__
93 static int      wlog_rec_pack(struct wlog_rec *wrec, char *buf, int flag);
94 static int      wlog_rec_unpack(struct wlog_rec *wrec, char *buf);
95 #else
96 static int      wlog_rec_pack();
97 static int      wlog_rec_unpack();
98 #endif
99
100 /*
101  * Initialize a write logfile.  wfile is a wlog_file structure that has
102  * the w_file field filled in.  The rest of the information in the
103  * structure is initialized by the routine.
104  *
105  * The trunc flag is used to indicate whether or not the logfile should
106  * be truncated if it currently exists.  If it is non-zero, the file will
107  * be truncated, otherwise it will be appended to.
108  *
109  * The mode argument is the [absolute] mode which the file will be
110  * given if it does not exist.  This mode is not affected by your process
111  * umask.
112  */
113
114 int
115 wlog_open(wfile, trunc, mode)
116 struct wlog_file        *wfile;
117 int                     trunc;
118 int                     mode;
119 {
120         int     omask, oflags;
121
122         if (trunc)
123                 trunc = O_TRUNC;
124
125         omask = umask(0);
126
127         /*
128          * Open 1 file descriptor as O_APPEND
129          */
130
131         oflags = O_WRONLY | O_APPEND | O_CREAT | trunc;
132         wfile->w_afd =
133                 open(wfile->w_file, oflags, mode);
134         umask(omask);
135
136         if (wfile->w_afd == -1) {
137                 sprintf(Wlog_Error_String,
138                         "Could not open write_log - open(%s, %#o, %#o) failed:  %s\n",
139                         wfile->w_file, oflags, mode, strerror(errno));
140                 return -1;
141         }
142
143         /*
144          * Open the next fd as a random access descriptor
145          */
146
147         oflags = O_RDWR;
148         if ((wfile->w_rfd = open(wfile->w_file, oflags)) == -1) {
149                 sprintf(Wlog_Error_String,
150                         "Could not open write log - open(%s, %#o) failed:  %s\n",
151                         wfile->w_file, oflags, strerror(errno));
152                 close(wfile->w_afd);
153                 wfile->w_afd = -1;
154                 return -1;
155         }
156     
157         return 0;
158 }
159
160 /*
161  * Release all resources associated with a wlog_file structure allocated
162  * with the wlog_open() call.
163  */
164
165 int
166 wlog_close(wfile)
167 struct wlog_file        *wfile;
168 {
169         close(wfile->w_afd);
170         close(wfile->w_rfd);
171         return 0;
172 }
173
174 /*
175  * Write a wlog_rec structure to a write logfile.  Offset is used to
176  * control where the record will be written.  If offset is < 0, the
177  * record will be appended to the end of the logfile.  Otherwise, the
178  * record which exists at the indicated offset will be overlayed.  This
179  * is so that we can record writes which are outstanding (with the w_done
180  * bit in wrec cleared), but not completed, and then later update the
181  * logfile when the write request completes (as with async io).  When
182  * offset is >= 0, only the fixed length portion of the record is 
183  * rewritten.  See text in write_log.h for details on the format of an
184  * on-disk record.
185  * 
186  * The return value of the function is the byte offset in the logfile
187  * where the record begins.
188  *
189  * Note:  It is the callers responsibility to make sure that the offset
190  * parameter 'points' to a valid record location when a record is to be
191  * overlayed.  This is guarenteed by saving the return value of a previous
192  * call to wlog_record_write() which wrote the record to be overlayed.
193  *
194  * Note2:  The on-disk version of the wlog_rec is MUCH different than
195  * the user version.  Don't expect to od the logfile and see data formatted
196  * as it is in the wlog_rec structure.  Considerable data packing takes
197  * place before the record is written.
198  */
199
200 int
201 wlog_record_write(wfile, wrec, offset)
202 struct wlog_file        *wfile;
203 struct wlog_rec         *wrec;
204 long                    offset;
205 {
206     int         reclen;
207     char        wbuf[WLOG_REC_MAX_SIZE + 2];
208
209     /*
210      * If offset is -1, we append the record at the end of file
211      *
212      * Otherwise, we overlay wrec at the file offset indicated and assume
213      * that the caller passed us the correct offset.  We do not record the
214      * fname in this case.
215      */
216
217     reclen = wlog_rec_pack(wrec, wbuf, (offset < 0));
218
219     if (offset < 0) {
220         /*
221          * Since we're writing a complete new record, we must also tack
222          * its length onto the end so that wlog_scan_backward() will work.
223          * Length is asumed to fit into 2 bytes.
224          */
225             
226             wbuf[reclen] = reclen / 256;
227             wbuf[reclen+1] = reclen % 256;
228             reclen += 2;
229
230             write(wfile->w_afd, wbuf, reclen);
231             offset = lseek(wfile->w_afd, 0, SEEK_CUR) - reclen;
232     } else {
233             lseek(wfile->w_rfd, offset, SEEK_SET);
234             write(wfile->w_rfd, wbuf, reclen);
235     }
236     
237     return offset;
238 }
239
240 /*
241  * Function to scan a logfile in reverse order.  Wfile is a valid
242  * wlog_file structure initialized by wlog_open().  nrecs is the number
243  * of records to scan (all records are scanned if nrecs is 0).  func is
244  * a user-supplied function to call for each record found.  The function
245  * will be passed a single parameter - a wlog_rec structure .
246  */
247
248 int
249 wlog_scan_backward(wfile, nrecs, func, data)
250 struct wlog_file        *wfile;
251 int                     nrecs;
252 int                     (*func)();
253 long                    data;
254 {
255         int                     fd, leftover, nbytes, offset, recnum, reclen;
256         char                    buf[BSIZE*32], *bufend, *cp, *bufstart;
257         char            albuf[WLOG_REC_MAX_SIZE];
258         struct wlog_rec wrec;
259
260         fd = wfile->w_rfd;
261
262         /*
263          * Move to EOF.  offset will always hold the current file offset
264          */
265
266         lseek(fd, 0, SEEK_END);
267         offset = lseek(fd, 0, SEEK_CUR);
268
269         bufend = buf + sizeof(buf);
270         bufstart = buf;
271
272         recnum = 0;
273         leftover = 0;
274         while ((!nrecs || recnum < nrecs) && offset > 0) {
275                 /*
276                  * Check for beginning of file - if there aren't enough bytes
277                  * remaining to fill buf, adjust bufstart.
278                  */
279
280                 if (offset + leftover < sizeof(buf)) {
281                         bufstart = bufend - (offset + leftover);
282                         offset = 0;
283                 } else {
284                         offset -= sizeof(buf) - leftover;
285                 }
286
287                 /* 
288                  * Move to the proper file offset, and read into buf
289                  */
290
291                 lseek(fd, offset, SEEK_SET);
292                 nbytes = read(fd, bufstart, bufend - bufstart - leftover);
293
294                 if (nbytes == -1) {
295                         sprintf(Wlog_Error_String,
296                                 "Could not read history file at offset %d - read(%d, %p, %d) failed:  %s\n",
297                                 offset, fd, bufstart,
298                                 (int)(bufend - bufstart - leftover), strerror(errno));
299                         return -1;
300                 }
301
302                 cp = bufend;
303                 leftover = 0;
304
305                 while (cp >= bufstart) {
306
307                         /*
308                          * If cp-bufstart is not large enough to hold a piece
309                          * of record length information, copy remainder to end
310                          * of buf and continue reading the file.
311                          */
312
313                         if (cp - bufstart < 2) {
314                                 leftover = cp - bufstart;
315                                 memcpy(bufend - leftover, bufstart, leftover);
316                                 break;
317                         }
318
319                         /*
320                          * Extract the record length.  We must do it this way
321                          * instead of casting cp to an int because cp might
322                          * not be word aligned.
323                          */
324
325                         reclen = (*(cp-2) * 256) + *(cp -1);
326
327                         /*
328                          * If cp-bufstart isn't large enough to hold a
329                          * complete record, plus the length information, copy
330                          * the leftover bytes to the end of buf and continue
331                          * reading.
332                          */
333
334                         if (cp - bufstart < reclen + 2) {
335                                 leftover = cp - bufstart;
336                                 memcpy(bufend - leftover, bufstart, leftover);
337                                 break;
338                         }
339
340                         /*
341                          * Adjust cp to point at the start of the record.
342                          * Copy the record into wbuf so that it is word
343                          * aligned and pass the record to the user supplied
344                          * function.
345                          */
346
347                         cp -= reclen + 2;
348                         memcpy(albuf, cp, reclen);
349
350                         wlog_rec_unpack(&wrec, albuf);
351
352                         /*
353                          * Call the user supplied function -
354                          * stop if instructed to.
355                          */
356
357                         if ((*func)(&wrec, data) == WLOG_STOP_SCAN) {
358                                 break;
359                         }
360
361                         recnum++;
362
363                         if (nrecs && recnum >= nrecs)
364                                 break;
365                 }
366         }
367
368         return 0;
369 }
370
371 /*
372  * The following 2 routines are used to pack and unpack the user
373  * visible wlog_rec structure to/from a character buffer which is
374  * stored or read from the write logfile.  Any changes to either of
375  * these routines must be reflected in the other.
376  */
377
378 static int
379 wlog_rec_pack(wrec, buf, flag)
380 struct wlog_rec *wrec;
381 char            *buf;
382 int             flag;
383 {
384         char                    *file, *host, *pattern;
385         struct wlog_rec_disk    *wrecd;
386
387         wrecd = (struct wlog_rec_disk *)buf;
388
389         wrecd->w_pid = (uint)wrec->w_pid;
390         wrecd->w_offset = (uint)wrec->w_offset;
391         wrecd->w_nbytes = (uint)wrec->w_nbytes;
392         wrecd->w_oflags = (uint)wrec->w_oflags;
393         wrecd->w_done = (uint)wrec->w_done;
394         wrecd->w_async = (uint)wrec->w_async;
395
396         wrecd->w_pathlen = (wrec->w_pathlen > 0) ? (uint)wrec->w_pathlen : 0;
397         wrecd->w_hostlen = (wrec->w_hostlen > 0) ? (uint)wrec->w_hostlen : 0;
398         wrecd->w_patternlen = (wrec->w_patternlen > 0) ? (uint)wrec->w_patternlen : 0;
399
400         /*
401          * If flag is true, we should also pack the variable length parts
402          * of the wlog_rec.  By default, we only pack the fixed length
403          * parts.
404          */
405
406         if (flag) {
407                 file = buf + sizeof(struct wlog_rec_disk);
408                 host = file + wrecd->w_pathlen;
409                 pattern = host + wrecd->w_hostlen;
410         
411                 if (wrecd->w_pathlen > 0)
412                         memcpy(file, wrec->w_path, wrecd->w_pathlen);
413         
414                 if (wrecd->w_hostlen > 0)
415                         memcpy(host, wrec->w_host, wrecd->w_hostlen);
416         
417                 if (wrecd->w_patternlen > 0)
418                         memcpy(pattern, wrec->w_pattern, wrecd->w_patternlen);
419
420                 return (sizeof(struct wlog_rec_disk) +
421                         wrecd->w_pathlen + wrecd->w_hostlen + wrecd->w_patternlen);
422         } else {
423                 return sizeof(struct wlog_rec_disk);
424         }
425 }
426
427 static int
428 wlog_rec_unpack(wrec, buf)
429 struct wlog_rec *wrec;
430 char            *buf;
431 {
432         char                    *file, *host, *pattern;
433         struct wlog_rec_disk    *wrecd;
434
435         bzero((char *)wrec, sizeof(struct wlog_rec));
436         wrecd = (struct wlog_rec_disk *)buf;
437
438         wrec->w_pid = wrecd->w_pid;
439         wrec->w_offset = wrecd->w_offset;
440         wrec->w_nbytes = wrecd->w_nbytes;
441         wrec->w_oflags = wrecd->w_oflags;
442         wrec->w_hostlen = wrecd->w_hostlen;
443         wrec->w_pathlen = wrecd->w_pathlen;
444         wrec->w_patternlen = wrecd->w_patternlen;
445         wrec->w_done = wrecd->w_done;
446         wrec->w_async = wrecd->w_async;
447
448         if (wrec->w_pathlen > 0) {
449                 file = buf + sizeof(struct wlog_rec_disk);
450                 memcpy(wrec->w_path, file, wrec->w_pathlen);
451         }
452
453         if (wrec->w_hostlen > 0) {
454                 host = buf + sizeof(struct wlog_rec_disk) + wrec->w_pathlen;
455                 memcpy(wrec->w_host, host, wrec->w_hostlen);
456         }
457
458         if (wrec->w_patternlen > 0) {
459                 pattern = buf + sizeof(struct wlog_rec_disk) +
460                         wrec->w_pathlen + wrec->w_hostlen;
461                 memcpy(wrec->w_pattern, pattern, wrec->w_patternlen);
462         }
463
464         return 0;
465 }