0bc24f1849e1f7782cee910e127b952eeb2841e9
[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 static char pbuffer[1024];
38
39 /*!
40  * \warning Beware, when using this function, of the header in front of most files.
41  */
42 static int
43 uphys_open(struct ubik_dbase *adbase, afs_int32 afid)
44 {
45     int fd;
46     static int initd;
47     int i;
48     struct fdcache *tfd;
49     struct fdcache *bestfd;
50
51     /* initialize package */
52     if (!initd) {
53         initd = 1;
54         tfd = fdcache;
55         for (i = 0; i < MAXFDCACHE; tfd++, i++) {
56             tfd->fd = -1;       /* invalid value */
57             tfd->fileID = -10000;       /* invalid value */
58             tfd->refCount = 0;
59         }
60     }
61
62     /* scan file descr cache */
63     for (tfd = fdcache, i = 0; i < MAXFDCACHE; i++, tfd++) {
64         if (afid == tfd->fileID && tfd->refCount == 0) {        /* don't use open fd */
65             lseek(tfd->fd, 0, 0);       /* reset ptr just like open would have */
66             tfd->refCount++;
67             return tfd->fd;
68         }
69     }
70
71     /* not found, open it and try to enter in cache */
72     snprintf(pbuffer, sizeof(pbuffer), "%s.DB%s%d", adbase->pathName,
73              (afid<0)?"SYS":"", (afid<0)?-afid:afid);
74     fd = open(pbuffer, O_CREAT | O_RDWR, 0600);
75     if (fd < 0) {
76         /* try opening read-only */
77         fd = open(pbuffer, O_RDONLY, 0);
78         if (fd < 0)
79             return fd;
80     }
81
82     /* enter it in the cache */
83     tfd = fdcache;
84     bestfd = NULL;
85     for (i = 0; i < MAXFDCACHE; i++, tfd++) {   /* look for empty slot */
86         if (tfd->fd == -1) {
87             bestfd = tfd;
88             break;
89         }
90     }
91     if (!bestfd) {              /* look for reclaimable slot */
92         tfd = fdcache;
93         for (i = 0; i < MAXFDCACHE; i++, tfd++) {
94             if (tfd->refCount == 0) {
95                 bestfd = tfd;
96                 break;
97             }
98         }
99     }
100     if (bestfd) {               /* found a usable slot */
101         tfd = bestfd;
102         if (tfd->fd >= 0)
103             close(tfd->fd);
104         tfd->fd = fd;
105         tfd->refCount = 1;      /* us */
106         tfd->fileID = afid;
107     }
108
109     /* finally, we're done */
110     return fd;
111 }
112
113 /*!
114  * \brief Close the file, maintaining ref count in cache structure.
115  */
116 static int
117 uphys_close(int afd)
118 {
119     int i;
120     struct fdcache *tfd;
121
122     if (afd < 0)
123         return EBADF;
124     tfd = fdcache;
125     for (i = 0; i < MAXFDCACHE; i++, tfd++) {
126         if (tfd->fd == afd) {
127             if (tfd->fileID != -10000) {
128                 tfd->refCount--;
129                 return 0;
130             } else {
131                 if (tfd->refCount > 0) {
132                     tfd->refCount--;
133                     if (tfd->refCount == 0) {
134                         close(tfd->fd);
135                         tfd->fd = -1;
136                     }
137                     return 0;
138                 }
139                 tfd->fd = -1;
140                 break;
141             }
142         }
143     }
144     return close(afd);
145 }
146
147 int
148 uphys_stat(struct ubik_dbase *adbase, afs_int32 afid, struct ubik_stat *astat)
149 {
150     int fd;
151     struct stat tstat;
152     afs_int32 code;
153
154     fd = uphys_open(adbase, afid);
155     if (fd < 0)
156         return fd;
157     code = fstat(fd, &tstat);
158     uphys_close(fd);
159     if (code < 0) {
160         return code;
161     }
162     code = tstat.st_size - HDRSIZE;
163     if (code < 0)
164         astat->size = 0;
165     else
166         astat->size = code;
167     return 0;
168 }
169
170 int
171 uphys_read(struct ubik_dbase *adbase, afs_int32 afile,
172            void *abuffer, afs_int32 apos, afs_int32 alength)
173 {
174     int fd;
175     afs_int32 code;
176
177     fd = uphys_open(adbase, afile);
178     if (fd < 0)
179         return -1;
180     code = lseek(fd, apos + HDRSIZE, 0);
181     if (code < 0) {
182         uphys_close(fd);
183         return -1;
184     }
185     code = read(fd, abuffer, alength);
186     uphys_close(fd);
187     return code;
188 }
189
190 int
191 uphys_write(struct ubik_dbase *adbase, afs_int32 afile,
192             void *abuffer, afs_int32 apos, afs_int32 alength)
193 {
194     int fd;
195     afs_int32 code;
196     afs_int32 length;
197
198     fd = uphys_open(adbase, afile);
199     if (fd < 0)
200         return -1;
201     code = lseek(fd, apos + HDRSIZE, 0);
202     if (code < 0) {
203         uphys_close(fd);
204         return -1;
205     }
206     length = write(fd, abuffer, alength);
207     code = uphys_close(fd);
208     if (code)
209         return -1;
210     else
211         return length;
212 }
213
214 int
215 uphys_truncate(struct ubik_dbase *adbase, afs_int32 afile,
216                afs_int32 asize)
217 {
218     afs_int32 code, fd;
219     fd = uphys_open(adbase, afile);
220     if (fd < 0)
221         return UNOENT;
222     code = ftruncate(fd, asize + HDRSIZE);
223     uphys_close(fd);
224     return code;
225 }
226
227 /*!
228  * \brief Get number of dbase files.
229  *
230  * \todo Really should scan dir for data.
231  */
232 int
233 uphys_getnfiles(struct ubik_dbase *adbase)
234 {
235     /* really should scan dir for data */
236     return 1;
237 }
238
239 /*!
240  * \brief Get database label, with \p aversion in host order.
241  */
242 int
243 uphys_getlabel(struct ubik_dbase *adbase, afs_int32 afile,
244                struct ubik_version *aversion)
245 {
246     struct ubik_hdr thdr;
247     afs_int32 code, fd;
248
249     fd = uphys_open(adbase, afile);
250     if (fd < 0)
251         return UNOENT;
252     code = read(fd, &thdr, sizeof(thdr));
253     if (code != sizeof(thdr)) {
254         uphys_close(fd);
255         return EIO;
256     }
257     aversion->epoch = ntohl(thdr.version.epoch);
258     aversion->counter = ntohl(thdr.version.counter);
259     uphys_close(fd);
260     return 0;
261 }
262
263 /*!
264  * \brief Label database, with \p aversion in host order.
265  */
266 int
267 uphys_setlabel(struct ubik_dbase *adbase, afs_int32 afile,
268                struct ubik_version *aversion)
269 {
270     struct ubik_hdr thdr;
271     afs_int32 code, fd;
272
273     fd = uphys_open(adbase, afile);
274     if (fd < 0)
275         return UNOENT;
276
277     memset(&thdr, 0, sizeof(thdr));
278
279     thdr.version.epoch = htonl(aversion->epoch);
280     thdr.version.counter = htonl(aversion->counter);
281     thdr.magic = htonl(UBIK_MAGIC);
282     thdr.size = htons(HDRSIZE);
283     code = write(fd, &thdr, sizeof(thdr));
284     fsync(fd);                  /* preserve over crash */
285     uphys_close(fd);
286     if (code != sizeof(thdr)) {
287         return EIO;
288     }
289     return 0;
290 }
291
292 int
293 uphys_sync(struct ubik_dbase *adbase, afs_int32 afile)
294 {
295     afs_int32 code, fd;
296     fd = uphys_open(adbase, afile);
297     code = fsync(fd);
298     uphys_close(fd);
299     return code;
300 }
301
302 void
303 uphys_invalidate(struct ubik_dbase *adbase, afs_int32 afid)
304 {
305     int i;
306     struct fdcache *tfd;
307
308     /* scan file descr cache */
309     for (tfd = fdcache, i = 0; i < MAXFDCACHE; i++, tfd++) {
310         if (afid == tfd->fileID) {
311             tfd->fileID = -10000;
312             if (tfd->fd >= 0 && tfd->refCount == 0) {
313                 close(tfd->fd);
314                 tfd->fd = -1;
315             }
316             return;
317         }
318     }
319 }