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