ubik: Rename flags to dbFlags
[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 #ifdef HAVE_PIO
50 # define uphys_pread  pread
51 # define uphys_pwrite pwrite
52 #else /* HAVE_PIO */
53 static_inline ssize_t
54 uphys_pread(int fd, void *buf, size_t nbyte, off_t offset)
55 {
56     if (lseek(fd, offset, 0) < 0) {
57         return -1;
58     }
59     return read(fd, buf, nbyte);
60 }
61
62 static_inline ssize_t
63 uphys_pwrite(int fd, void *buf, size_t nbyte, off_t offset)
64 {
65     if (lseek(fd, offset, 0) < 0) {
66         return -1;
67     }
68     return write(fd, buf, nbyte);
69 }
70 #endif /* !HAVE_PIO */
71
72 /*!
73  * \warning Beware, when using this function, of the header in front of most files.
74  */
75 static int
76 uphys_open(struct ubik_dbase *adbase, afs_int32 afid)
77 {
78     int fd;
79     static int initd;
80     int i;
81     struct fdcache *tfd;
82     struct fdcache *bestfd;
83
84     /* initialize package */
85     if (!initd) {
86         initd = 1;
87         tfd = fdcache;
88         for (i = 0; i < MAXFDCACHE; tfd++, i++) {
89             tfd->fd = -1;       /* invalid value */
90             tfd->fileID = -10000;       /* invalid value */
91             tfd->refCount = 0;
92         }
93     }
94
95     /* scan file descr cache */
96     for (tfd = fdcache, i = 0; i < MAXFDCACHE; i++, tfd++) {
97         if (afid == tfd->fileID && tfd->refCount == 0) {        /* don't use open fd */
98             tfd->refCount++;
99             return tfd->fd;
100         }
101     }
102
103     /* not found, open it and try to enter in cache */
104     snprintf(pbuffer, sizeof(pbuffer), "%s.DB%s%d", adbase->pathName,
105              (afid<0)?"SYS":"", (afid<0)?-afid:afid);
106     fd = open(pbuffer, O_CREAT | O_RDWR, 0600);
107     if (fd < 0) {
108         /* try opening read-only */
109         fd = open(pbuffer, O_RDONLY, 0);
110         if (fd < 0)
111             return fd;
112     }
113
114     /* enter it in the cache */
115     tfd = fdcache;
116     bestfd = NULL;
117     for (i = 0; i < MAXFDCACHE; i++, tfd++) {   /* look for empty slot */
118         if (tfd->fd == -1) {
119             bestfd = tfd;
120             break;
121         }
122     }
123     if (!bestfd) {              /* look for reclaimable slot */
124         tfd = fdcache;
125         for (i = 0; i < MAXFDCACHE; i++, tfd++) {
126             if (tfd->refCount == 0) {
127                 bestfd = tfd;
128                 break;
129             }
130         }
131     }
132     if (bestfd) {               /* found a usable slot */
133         tfd = bestfd;
134         if (tfd->fd >= 0)
135             close(tfd->fd);
136         tfd->fd = fd;
137         tfd->refCount = 1;      /* us */
138         tfd->fileID = afid;
139     }
140
141     /* finally, we're done */
142     return fd;
143 }
144
145 /*!
146  * \brief Close the file, maintaining ref count in cache structure.
147  */
148 static int
149 uphys_close(int afd)
150 {
151     int i;
152     struct fdcache *tfd;
153
154     if (afd < 0)
155         return EBADF;
156     tfd = fdcache;
157     for (i = 0; i < MAXFDCACHE; i++, tfd++) {
158         if (tfd->fd == afd) {
159             if (tfd->fileID != -10000) {
160                 tfd->refCount--;
161                 return 0;
162             } else {
163                 if (tfd->refCount > 0) {
164                     tfd->refCount--;
165                     if (tfd->refCount == 0) {
166                         close(tfd->fd);
167                         tfd->fd = -1;
168                     }
169                     return 0;
170                 }
171                 tfd->fd = -1;
172                 break;
173             }
174         }
175     }
176     return close(afd);
177 }
178
179 int
180 uphys_stat(struct ubik_dbase *adbase, afs_int32 afid, struct ubik_stat *astat)
181 {
182     int fd;
183     struct stat tstat;
184     afs_int32 code;
185
186     fd = uphys_open(adbase, afid);
187     if (fd < 0)
188         return fd;
189     code = fstat(fd, &tstat);
190     uphys_close(fd);
191     if (code < 0) {
192         return code;
193     }
194     code = tstat.st_size - HDRSIZE;
195     if (code < 0)
196         astat->size = 0;
197     else
198         astat->size = code;
199     return 0;
200 }
201
202 int
203 uphys_read(struct ubik_dbase *adbase, afs_int32 afile,
204            void *abuffer, afs_int32 apos, afs_int32 alength)
205 {
206     int fd;
207     afs_int32 code;
208
209     fd = uphys_open(adbase, afile);
210     if (fd < 0)
211         return -1;
212     code = uphys_pread(fd, abuffer, alength, apos + HDRSIZE);
213     uphys_close(fd);
214     return code;
215 }
216
217 int
218 uphys_write(struct ubik_dbase *adbase, afs_int32 afile,
219             void *abuffer, afs_int32 apos, afs_int32 alength)
220 {
221     int fd;
222     afs_int32 code;
223     afs_int32 length;
224
225     fd = uphys_open(adbase, afile);
226     if (fd < 0)
227         return -1;
228     length = uphys_pwrite(fd, abuffer, alength, apos + HDRSIZE);
229     code = uphys_close(fd);
230     if (code)
231         return -1;
232     else
233         return length;
234 }
235
236 int
237 uphys_truncate(struct ubik_dbase *adbase, afs_int32 afile,
238                afs_int32 asize)
239 {
240     afs_int32 code, fd;
241
242     /* Just in case there's memory-buffered data for this file, flush it before
243      * truncating. */
244     if (uphys_buf_flush(adbase, afile) < 0) {
245         return UIOERROR;
246     }
247
248     fd = uphys_open(adbase, afile);
249     if (fd < 0)
250         return UNOENT;
251     code = ftruncate(fd, asize + HDRSIZE);
252     uphys_close(fd);
253     return code;
254 }
255
256 /*!
257  * \brief Get number of dbase files.
258  *
259  * \todo Really should scan dir for data.
260  */
261 int
262 uphys_getnfiles(struct ubik_dbase *adbase)
263 {
264     /* really should scan dir for data */
265     return 1;
266 }
267
268 /*!
269  * \brief Get database label, with \p aversion in host order.
270  */
271 int
272 uphys_getlabel(struct ubik_dbase *adbase, afs_int32 afile,
273                struct ubik_version *aversion)
274 {
275     struct ubik_hdr thdr;
276     afs_int32 code, fd;
277
278     fd = uphys_open(adbase, afile);
279     if (fd < 0)
280         return UNOENT;
281     code = uphys_pread(fd, &thdr, sizeof(thdr), 0);
282     if (code != sizeof(thdr)) {
283         uphys_close(fd);
284         return EIO;
285     }
286     aversion->epoch = ntohl(thdr.version.epoch);
287     aversion->counter = ntohl(thdr.version.counter);
288     uphys_close(fd);
289     return 0;
290 }
291
292 /*!
293  * \brief Label database, with \p aversion in host order.
294  */
295 int
296 uphys_setlabel(struct ubik_dbase *adbase, afs_int32 afile,
297                struct ubik_version *aversion)
298 {
299     struct ubik_hdr thdr;
300     afs_int32 code, fd;
301
302     fd = uphys_open(adbase, afile);
303     if (fd < 0)
304         return UNOENT;
305
306     memset(&thdr, 0, sizeof(thdr));
307
308     thdr.version.epoch = htonl(aversion->epoch);
309     thdr.version.counter = htonl(aversion->counter);
310     thdr.magic = htonl(UBIK_MAGIC);
311     thdr.size = htons(HDRSIZE);
312     code = uphys_pwrite(fd, &thdr, sizeof(thdr), 0);
313     fsync(fd);                  /* preserve over crash */
314     uphys_close(fd);
315     if (code != sizeof(thdr)) {
316         return EIO;
317     }
318     return 0;
319 }
320
321 int
322 uphys_sync(struct ubik_dbase *adbase, afs_int32 afile)
323 {
324     afs_int32 code, fd;
325
326     /* Flush any in-memory data, so we can sync it. */
327     if (uphys_buf_flush(adbase, afile) < 0) {
328         return -1;
329     }
330
331     fd = uphys_open(adbase, afile);
332     code = fsync(fd);
333     uphys_close(fd);
334     return code;
335 }
336
337 void
338 uphys_invalidate(struct ubik_dbase *adbase, afs_int32 afid)
339 {
340     int i;
341     struct fdcache *tfd;
342
343     /* scan file descr cache */
344     for (tfd = fdcache, i = 0; i < MAXFDCACHE; i++, tfd++) {
345         if (afid == tfd->fileID) {
346             tfd->fileID = -10000;
347             if (tfd->fd >= 0 && tfd->refCount == 0) {
348                 close(tfd->fd);
349                 tfd->fd = -1;
350             }
351             return;
352         }
353     }
354 }
355
356 static FILE *
357 uphys_buf_append_open(struct ubik_dbase *adbase, afs_int32 afid)
358 {
359     /* If we have a cached handle open for this file, just return it. */
360     if (buf_fdcache.stream && buf_fdcache.fileID == afid) {
361         return buf_fdcache.stream;
362     }
363
364     /* Otherwise, close the existing handle, and open a new handle for the
365      * given file. */
366
367     if (buf_fdcache.stream) {
368         fclose(buf_fdcache.stream);
369     }
370
371     snprintf(pbuffer, sizeof(pbuffer), "%s.DB%s%d", adbase->pathName,
372              (afid<0)?"SYS":"", (afid<0)?-afid:afid);
373     buf_fdcache.stream = fopen(pbuffer, "a");
374     buf_fdcache.fileID = afid;
375     return buf_fdcache.stream;
376 }
377
378 static int
379 uphys_buf_flush(struct ubik_dbase *adbase, afs_int32 afid)
380 {
381     if (buf_fdcache.stream && buf_fdcache.fileID == afid) {
382         int code = fflush(buf_fdcache.stream);
383         if (code) {
384             return -1;
385         }
386     }
387     return 0;
388 }
389
390 /* Append the given data to the given database file, allowing the data to be
391  * buffered in memory. */
392 int
393 uphys_buf_append(struct ubik_dbase *adbase, afs_int32 afid, void *adata,
394                  afs_int32 alength)
395 {
396     FILE *stream;
397     size_t code;
398
399     stream = uphys_buf_append_open(adbase, afid);
400     if (!stream) {
401         return -1;
402     }
403
404     code = fwrite(adata, alength, 1, stream);
405     if (code != 1) {
406         return -1;
407     }
408     return alength;
409 }