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