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