bea42cf00f62156e5dfce6a31d930082d3a9e7a8
[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 }