b8fbef4ebd5671a6bede085d9b2ac90ef8db16e1
[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     astat->mtime = tstat.st_mtime;
163     code = tstat.st_size - HDRSIZE;
164     if (code < 0)
165         astat->size = 0;
166     else
167         astat->size = code;
168     return 0;
169 }
170
171 int
172 uphys_read(struct ubik_dbase *adbase, afs_int32 afile,
173            void *abuffer, afs_int32 apos, afs_int32 alength)
174 {
175     int fd;
176     afs_int32 code;
177
178     fd = uphys_open(adbase, afile);
179     if (fd < 0)
180         return -1;
181     code = lseek(fd, apos + HDRSIZE, 0);
182     if (code < 0) {
183         uphys_close(fd);
184         return -1;
185     }
186     code = read(fd, abuffer, alength);
187     uphys_close(fd);
188     return code;
189 }
190
191 int
192 uphys_write(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     afs_int32 length;
198
199     fd = uphys_open(adbase, afile);
200     if (fd < 0)
201         return -1;
202     code = lseek(fd, apos + HDRSIZE, 0);
203     if (code < 0) {
204         uphys_close(fd);
205         return -1;
206     }
207     length = write(fd, abuffer, alength);
208     code = uphys_close(fd);
209     if (code)
210         return -1;
211     else
212         return length;
213 }
214
215 int
216 uphys_truncate(struct ubik_dbase *adbase, afs_int32 afile,
217                afs_int32 asize)
218 {
219     afs_int32 code, fd;
220     fd = uphys_open(adbase, afile);
221     if (fd < 0)
222         return UNOENT;
223     code = ftruncate(fd, asize + HDRSIZE);
224     uphys_close(fd);
225     return code;
226 }
227
228 /*!
229  * \brief Get number of dbase files.
230  *
231  * \todo Really should scan dir for data.
232  */
233 int
234 uphys_getnfiles(struct ubik_dbase *adbase)
235 {
236     /* really should scan dir for data */
237     return 1;
238 }
239
240 /*!
241  * \brief Get database label, with \p aversion in host order.
242  */
243 int
244 uphys_getlabel(struct ubik_dbase *adbase, afs_int32 afile,
245                struct ubik_version *aversion)
246 {
247     struct ubik_hdr thdr;
248     afs_int32 code, fd;
249
250     fd = uphys_open(adbase, afile);
251     if (fd < 0)
252         return UNOENT;
253     code = read(fd, &thdr, sizeof(thdr));
254     if (code != sizeof(thdr)) {
255         uphys_close(fd);
256         return EIO;
257     }
258     aversion->epoch = ntohl(thdr.version.epoch);
259     aversion->counter = ntohl(thdr.version.counter);
260     uphys_close(fd);
261     return 0;
262 }
263
264 /*!
265  * \brief Label database, with \p aversion in host order.
266  */
267 int
268 uphys_setlabel(struct ubik_dbase *adbase, afs_int32 afile,
269                struct ubik_version *aversion)
270 {
271     struct ubik_hdr thdr;
272     afs_int32 code, fd;
273
274     fd = uphys_open(adbase, afile);
275     if (fd < 0)
276         return UNOENT;
277     thdr.version.epoch = htonl(aversion->epoch);
278     thdr.version.counter = htonl(aversion->counter);
279     thdr.magic = htonl(UBIK_MAGIC);
280     thdr.size = htons(HDRSIZE);
281     code = write(fd, &thdr, sizeof(thdr));
282     fsync(fd);                  /* preserve over crash */
283     uphys_close(fd);
284     if (code != sizeof(thdr)) {
285         return EIO;
286     }
287     return 0;
288 }
289
290 int
291 uphys_sync(struct ubik_dbase *adbase, afs_int32 afile)
292 {
293     afs_int32 code, fd;
294     fd = uphys_open(adbase, afile);
295     code = fsync(fd);
296     uphys_close(fd);
297     return code;
298 }
299
300 void
301 uphys_invalidate(struct ubik_dbase *adbase, afs_int32 afid)
302 {
303     int i;
304     struct fdcache *tfd;
305
306     /* scan file descr cache */
307     for (tfd = fdcache, i = 0; i < MAXFDCACHE; i++, tfd++) {
308         if (afid == tfd->fileID) {
309             tfd->fileID = -10000;
310             if (tfd->fd >= 0 && tfd->refCount == 0) {
311                 close(tfd->fd);
312                 tfd->fd = -1;
313             }
314             return;
315         }
316     }
317 }