1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2007 Diomidis Spinellis
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 */
29
30#include <sys/param.h>
31#include <sys/stat.h>
32#include <sys/types.h>
33#include <sys/acct.h>
34
35#include <errno.h>
36#include <stddef.h>
37#include <stdio.h>
38#include <string.h>
39
40int	 readrec_forward(FILE *f, struct acctv3 *av2);
41int	 readrec_backward(FILE *f, struct acctv3 *av2);
42
43/*
44 * Reverse offsetof: return the offset of field f
45 * from the end of the structure s.
46 */
47#define roffsetof(s, f) (sizeof(s) - offsetof(s, f))
48
49/*
50 * Read exactly one record of size size from stream f into ptr.
51 * Failure to read the complete record is considered a file format error,
52 * and will set errno to EFTYPE.
53 * Return 0 on success, EOF on end of file or error.
54 */
55static int
56fread_record(void *ptr, size_t size, FILE *f)
57{
58	size_t rv;
59
60	if ((rv = fread(ptr, 1, size, f)) == size)
61		return (0);
62	else if (ferror(f) || rv == 0)
63		return (EOF);
64	else {
65		/* Short read. */
66		errno = EFTYPE;
67		return (EOF);
68	}
69}
70
71/*
72 * Return the value of a comp_t field.
73 */
74static float
75decode_comp(comp_t v)
76{
77	int result, exp;
78
79	result = v & 017777;
80	for (exp = v >> 13; exp; exp--)
81		result <<= 3;
82	return ((double)result / AHZV1);
83}
84
85/*
86 * Read a v1 accounting record stored at the current
87 * position of stream f.
88 * Convert the data to the current record format.
89 * Return EOF on error or end-of-file.
90 */
91static int
92readrec_v1(FILE *f, struct acctv3 *av3)
93{
94	struct acctv1 av1;
95	int rv;
96
97	if ((rv = fread_record(&av1, sizeof(av1), f)) == EOF)
98		return (EOF);
99	av3->ac_zero = 0;
100	av3->ac_version = 3;
101	av3->ac_len = av3->ac_len2 = sizeof(*av3);
102	memcpy(av3->ac_comm, av1.ac_comm, AC_COMM_LEN);
103	av3->ac_utime = decode_comp(av1.ac_utime) * 1000000;
104	av3->ac_stime = decode_comp(av1.ac_stime) * 1000000;
105	av3->ac_etime = decode_comp(av1.ac_etime) * 1000000;
106	av3->ac_btime = av1.ac_btime;
107	av3->ac_uid = av1.ac_uid;
108	av3->ac_gid = av1.ac_gid;
109	av3->ac_mem = av1.ac_mem;
110	av3->ac_io = decode_comp(av1.ac_io);
111	av3->ac_tty = av1.ac_tty;
112	av3->ac_flagx = av1.ac_flag | ANVER;
113	return (0);
114}
115
116/*
117 * Read an v2 accounting record stored at the current
118 * position of stream f.
119 * Return EOF on error or end-of-file.
120 */
121static int
122readrec_v2(FILE *f, struct acctv3 *av3)
123{
124	struct acctv2 av2;
125	int rv;
126
127	if ((rv = fread_record(&av2, sizeof(av2), f)) == EOF)
128		return (EOF);
129	av3->ac_zero = 0;
130	av3->ac_version = 3;
131	av3->ac_len = av3->ac_len2 = sizeof(*av3);
132	memcpy(av3->ac_comm, av2.ac_comm, AC_COMM_LEN);
133	av3->ac_utime = av2.ac_utime;
134	av3->ac_stime = av2.ac_stime;
135	av3->ac_etime = av2.ac_etime;
136	av3->ac_btime = av2.ac_btime;
137	av3->ac_uid = av2.ac_uid;
138	av3->ac_gid = av2.ac_gid;
139	av3->ac_mem = av2.ac_mem;
140	av3->ac_io = av2.ac_io;
141	av3->ac_tty = av2.ac_tty;
142	av3->ac_flagx = av2.ac_flagx;
143	return (0);
144}
145
146/*
147 * Read an v2 accounting record stored at the current
148 * position of stream f.
149 * Return EOF on error or end-of-file.
150 */
151static int
152readrec_v3(FILE *f, struct acctv3 *av3)
153{
154
155	return (fread_record(av3, sizeof(*av3), f));
156}
157
158/*
159 * Read a new-style (post-v1) accounting record stored at
160 * the current position of stream f.
161 * Convert the data to the current record format.
162 * Return EOF on error or end-of-file.
163 */
164static int
165readrec_vx(FILE *f, struct acctv3 *av3)
166{
167	uint8_t magic, version;
168
169	if (fread_record(&magic, sizeof(magic), f) == EOF ||
170	    fread_record(&version, sizeof(version), f) == EOF ||
171	    ungetc(version, f) == EOF ||
172	    ungetc(magic, f) == EOF)
173		return (EOF);
174	switch (version) {
175	case 2:
176		return (readrec_v2(f, av3));
177	case 3:
178		return (readrec_v3(f, av3));
179
180	/* Add handling for more versions here. */
181
182	default:
183		errno = EFTYPE;
184		return (EOF);
185	}
186}
187
188/*
189 * Read an accounting record stored at the current
190 * position of stream f.
191 * Old-format records are converted to the current record
192 * format.
193 * Return the number of records read (1 or 0 at the end-of-file),
194 * or EOF on error.
195 */
196int
197readrec_forward(FILE *f, struct acctv3 *av3)
198{
199	int magic, rv;
200
201	if ((magic = getc(f)) == EOF)
202		return (ferror(f) ? EOF : 0);
203	if (ungetc(magic, f) == EOF)
204		return (EOF);
205	if (magic != 0)
206		/* Old record format. */
207		rv = readrec_v1(f, av3);
208	else
209		/* New record formats. */
210		rv = readrec_vx(f, av3);
211	return (rv == EOF ? EOF : 1);
212}
213
214/*
215 * Read an accounting record ending at the current
216 * position of stream f.
217 * Old-format records are converted to the current record
218 * format.
219 * The file pointer is positioned at the beginning of the
220 * record read.
221 * Return the number of records read (1 or 0 at the end-of-file),
222 * or EOF on error.
223 */
224int
225readrec_backward(FILE *f, struct acctv3 *av3)
226{
227	off_t pos;
228	int c;
229	uint16_t len;
230
231	if ((pos = ftell(f)) == -1)
232		return (EOF);
233	if (pos == 0)
234		return (0);
235	if (fseek(f, -roffsetof(struct acctv3, ac_trailer),
236	    SEEK_CUR) == EOF ||
237	    (c = getc(f)) == EOF)
238		return (EOF);
239	if (c & ANVER) {
240		/*
241		 * New record formats.  For v2 and v3 offset from the
242		 * end for ac_len2 should be same.
243		 */
244		if (fseeko(f, pos - roffsetof(struct acctv2, ac_len2),
245		    SEEK_SET) == EOF ||
246		    fread_record(&len, sizeof(len), f) == EOF ||
247		    fseeko(f, pos - len, SEEK_SET) == EOF ||
248		    readrec_vx(f, av3) == EOF ||
249		    fseeko(f, pos - len, SEEK_SET) == EOF)
250			return (EOF);
251		else
252			return (1);
253	} else {
254		/* Old record format. */
255		if (fseeko(f, pos - sizeof(struct acctv1), SEEK_SET) == EOF ||
256		    readrec_v1(f, av3) == EOF ||
257		    fseeko(f, pos - sizeof(struct acctv1), SEEK_SET) == EOF)
258			return (EOF);
259		else
260			return (1);
261	}
262}
263