Import Upstream version 1.8.5
[hcoop/debian/openafs.git] / src / ubik / phys.c
1 /*
2 * Copyright 2000, International Business Machines Corporation and others.
3 * All Rights Reserved.
4 *
5 * This software has been released under the terms of the IBM Public
6 * License. For details, see the LICENSE file in the top-level source
7 * directory or online at http://www.openafs.org/dl/license10.html
8 */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 #include <roken.h>
14
15 #include <lwp.h>
16
17 #include <lock.h>
18 #include <afs/afsutil.h>
19
20 #define UBIK_INTERNALS 1
21 #include "ubik.h"
22
23 /*! \file
24 * These routines are called via the proc ptr in the ubik_dbase structure. They provide access to
25 * the physical disk, by converting the file numbers being processed ( >= 0 for user data space, < 0
26 * for ubik system files, such as the log) to actual pathnames to open, read, write, truncate, sync,
27 * etc.
28 */
29
30 #define MAXFDCACHE 4
31 static struct fdcache {
32 int fd;
33 int fileID;
34 int refCount;
35 } fdcache[MAXFDCACHE];
36
37 /* Cache a stdio handle for a given database file, for uphys_buf_append
38 * operations. We only use buf_append for one file at a time, so only try to
39 * cache a single file handle, since that's all we should need. */
40 static struct buf_fdcache {
41 int fileID;
42 FILE *stream;
43 } buf_fdcache;
44
45 static char pbuffer[1024];
46
47 static int uphys_buf_flush(struct ubik_dbase *adbase, afs_int32 afid);
48
49 /*!
50 * \warning Beware, when using this function, of the header in front of most files.
51 */
52 static int
53 uphys_open(struct ubik_dbase *adbase, afs_int32 afid)
54 {
55 int fd;
56 static int initd;
57 int i;
58 struct fdcache *tfd;
59 struct fdcache *bestfd;
60
61 /* initialize package */
62 if (!initd) {
63 initd = 1;
64 tfd = fdcache;
65 for (i = 0; i < MAXFDCACHE; tfd++, i++) {
66 tfd->fd = -1; /* invalid value */
67 tfd->fileID = -10000; /* invalid value */
68 tfd->refCount = 0;
69 }
70 }
71
72 /* scan file descr cache */
73 for (tfd = fdcache, i = 0; i < MAXFDCACHE; i++, tfd++) {
74 if (afid == tfd->fileID && tfd->refCount == 0) { /* don't use open fd */
75 lseek(tfd->fd, 0, 0); /* reset ptr just like open would have */
76 tfd->refCount++;
77 return tfd->fd;
78 }
79 }
80
81 /* not found, open it and try to enter in cache */
82 snprintf(pbuffer, sizeof(pbuffer), "%s.DB%s%d", adbase->pathName,
83 (afid<0)?"SYS":"", (afid<0)?-afid:afid);
84 fd = open(pbuffer, O_CREAT | O_RDWR, 0600);
85 if (fd < 0) {
86 /* try opening read-only */
87 fd = open(pbuffer, O_RDONLY, 0);
88 if (fd < 0)
89 return fd;
90 }
91
92 /* enter it in the cache */
93 tfd = fdcache;
94 bestfd = NULL;
95 for (i = 0; i < MAXFDCACHE; i++, tfd++) { /* look for empty slot */
96 if (tfd->fd == -1) {
97 bestfd = tfd;
98 break;
99 }
100 }
101 if (!bestfd) { /* look for reclaimable slot */
102 tfd = fdcache;
103 for (i = 0; i < MAXFDCACHE; i++, tfd++) {
104 if (tfd->refCount == 0) {
105 bestfd = tfd;
106 break;
107 }
108 }
109 }
110 if (bestfd) { /* found a usable slot */
111 tfd = bestfd;
112 if (tfd->fd >= 0)
113 close(tfd->fd);
114 tfd->fd = fd;
115 tfd->refCount = 1; /* us */
116 tfd->fileID = afid;
117 }
118
119 /* finally, we're done */
120 return fd;
121 }
122
123 /*!
124 * \brief Close the file, maintaining ref count in cache structure.
125 */
126 static int
127 uphys_close(int afd)
128 {
129 int i;
130 struct fdcache *tfd;
131
132 if (afd < 0)
133 return EBADF;
134 tfd = fdcache;
135 for (i = 0; i < MAXFDCACHE; i++, tfd++) {
136 if (tfd->fd == afd) {
137 if (tfd->fileID != -10000) {
138 tfd->refCount--;
139 return 0;
140 } else {
141 if (tfd->refCount > 0) {
142 tfd->refCount--;
143 if (tfd->refCount == 0) {
144 close(tfd->fd);
145 tfd->fd = -1;
146 }
147 return 0;
148 }
149 tfd->fd = -1;
150 break;
151 }
152 }
153 }
154 return close(afd);
155 }
156
157 int
158 uphys_stat(struct ubik_dbase *adbase, afs_int32 afid, struct ubik_stat *astat)
159 {
160 int fd;
161 struct stat tstat;
162 afs_int32 code;
163
164 fd = uphys_open(adbase, afid);
165 if (fd < 0)
166 return fd;
167 code = fstat(fd, &tstat);
168 uphys_close(fd);
169 if (code < 0) {
170 return code;
171 }
172 code = tstat.st_size - HDRSIZE;
173 if (code < 0)
174 astat->size = 0;
175 else
176 astat->size = code;
177 return 0;
178 }
179
180 int
181 uphys_read(struct ubik_dbase *adbase, afs_int32 afile,
182 void *abuffer, afs_int32 apos, afs_int32 alength)
183 {
184 int fd;
185 afs_int32 code;
186
187 fd = uphys_open(adbase, afile);
188 if (fd < 0)
189 return -1;
190 code = lseek(fd, apos + HDRSIZE, 0);
191 if (code < 0) {
192 uphys_close(fd);
193 return -1;
194 }
195 code = read(fd, abuffer, alength);
196 uphys_close(fd);
197 return code;
198 }
199
200 int
201 uphys_write(struct ubik_dbase *adbase, afs_int32 afile,
202 void *abuffer, afs_int32 apos, afs_int32 alength)
203 {
204 int fd;
205 afs_int32 code;
206 afs_int32 length;
207
208 fd = uphys_open(adbase, afile);
209 if (fd < 0)
210 return -1;
211 code = lseek(fd, apos + HDRSIZE, 0);
212 if (code < 0) {
213 uphys_close(fd);
214 return -1;
215 }
216 length = write(fd, abuffer, alength);
217 code = uphys_close(fd);
218 if (code)
219 return -1;
220 else
221 return length;
222 }
223
224 int
225 uphys_truncate(struct ubik_dbase *adbase, afs_int32 afile,
226 afs_int32 asize)
227 {
228 afs_int32 code, fd;
229
230 /* Just in case there's memory-buffered data for this file, flush it before
231 * truncating. */
232 if (uphys_buf_flush(adbase, afile) < 0) {
233 return UIOERROR;
234 }
235
236 fd = uphys_open(adbase, afile);
237 if (fd < 0)
238 return UNOENT;
239 code = ftruncate(fd, asize + HDRSIZE);
240 uphys_close(fd);
241 return code;
242 }
243
244 /*!
245 * \brief Get number of dbase files.
246 *
247 * \todo Really should scan dir for data.
248 */
249 int
250 uphys_getnfiles(struct ubik_dbase *adbase)
251 {
252 /* really should scan dir for data */
253 return 1;
254 }
255
256 /*!
257 * \brief Get database label, with \p aversion in host order.
258 */
259 int
260 uphys_getlabel(struct ubik_dbase *adbase, afs_int32 afile,
261 struct ubik_version *aversion)
262 {
263 struct ubik_hdr thdr;
264 afs_int32 code, fd;
265
266 fd = uphys_open(adbase, afile);
267 if (fd < 0)
268 return UNOENT;
269 code = read(fd, &thdr, sizeof(thdr));
270 if (code != sizeof(thdr)) {
271 uphys_close(fd);
272 return EIO;
273 }
274 aversion->epoch = ntohl(thdr.version.epoch);
275 aversion->counter = ntohl(thdr.version.counter);
276 uphys_close(fd);
277 return 0;
278 }
279
280 /*!
281 * \brief Label database, with \p aversion in host order.
282 */
283 int
284 uphys_setlabel(struct ubik_dbase *adbase, afs_int32 afile,
285 struct ubik_version *aversion)
286 {
287 struct ubik_hdr thdr;
288 afs_int32 code, fd;
289
290 fd = uphys_open(adbase, afile);
291 if (fd < 0)
292 return UNOENT;
293
294 memset(&thdr, 0, sizeof(thdr));
295
296 thdr.version.epoch = htonl(aversion->epoch);
297 thdr.version.counter = htonl(aversion->counter);
298 thdr.magic = htonl(UBIK_MAGIC);
299 thdr.size = htons(HDRSIZE);
300 code = write(fd, &thdr, sizeof(thdr));
301 fsync(fd); /* preserve over crash */
302 uphys_close(fd);
303 if (code != sizeof(thdr)) {
304 return EIO;
305 }
306 return 0;
307 }
308
309 int
310 uphys_sync(struct ubik_dbase *adbase, afs_int32 afile)
311 {
312 afs_int32 code, fd;
313
314 /* Flush any in-memory data, so we can sync it. */
315 if (uphys_buf_flush(adbase, afile) < 0) {
316 return -1;
317 }
318
319 fd = uphys_open(adbase, afile);
320 code = fsync(fd);
321 uphys_close(fd);
322 return code;
323 }
324
325 void
326 uphys_invalidate(struct ubik_dbase *adbase, afs_int32 afid)
327 {
328 int i;
329 struct fdcache *tfd;
330
331 /* scan file descr cache */
332 for (tfd = fdcache, i = 0; i < MAXFDCACHE; i++, tfd++) {
333 if (afid == tfd->fileID) {
334 tfd->fileID = -10000;
335 if (tfd->fd >= 0 && tfd->refCount == 0) {
336 close(tfd->fd);
337 tfd->fd = -1;
338 }
339 return;
340 }
341 }
342 }
343
344 static FILE *
345 uphys_buf_append_open(struct ubik_dbase *adbase, afs_int32 afid)
346 {
347 /* If we have a cached handle open for this file, just return it. */
348 if (buf_fdcache.stream && buf_fdcache.fileID == afid) {
349 return buf_fdcache.stream;
350 }
351
352 /* Otherwise, close the existing handle, and open a new handle for the
353 * given file. */
354
355 if (buf_fdcache.stream) {
356 fclose(buf_fdcache.stream);
357 }
358
359 snprintf(pbuffer, sizeof(pbuffer), "%s.DB%s%d", adbase->pathName,
360 (afid<0)?"SYS":"", (afid<0)?-afid:afid);
361 buf_fdcache.stream = fopen(pbuffer, "a");
362 buf_fdcache.fileID = afid;
363 return buf_fdcache.stream;
364 }
365
366 static int
367 uphys_buf_flush(struct ubik_dbase *adbase, afs_int32 afid)
368 {
369 if (buf_fdcache.stream && buf_fdcache.fileID == afid) {
370 int code = fflush(buf_fdcache.stream);
371 if (code) {
372 return -1;
373 }
374 }
375 return 0;
376 }
377
378 /* Append the given data to the given database file, allowing the data to be
379 * buffered in memory. */
380 int
381 uphys_buf_append(struct ubik_dbase *adbase, afs_int32 afid, void *adata,
382 afs_int32 alength)
383 {
384 FILE *stream;
385 size_t code;
386
387 stream = uphys_buf_append_open(adbase, afid);
388 if (!stream) {
389 return -1;
390 }
391
392 code = fwrite(adata, alength, 1, stream);
393 if (code != 1) {
394 return -1;
395 }
396 return alength;
397 }