source: trunk/athena/bin/discuss/server/atom.c @ 17452

Revision 17452, 8.2 KB checked in by zacheiss, 22 years ago (diff)
Include unistd.h for lseek prototype and SEEK_* macros
Line 
1/*
2 *
3 *      Copyright (C) 1988, 1989 by the Massachusetts Institute of Technology
4 *      Developed by the MIT Student Information Processing Board (SIPB).
5 *      For copying information, see the file mit-copyright.h in this release.
6 *
7 */
8/*
9 *
10 * atom.c --    File to support the idea of 'atomic' files in C.  These are
11 *              files that support the ideas of coordination and failure
12 *              atomicity.  Coordination is handled by using flock on the
13 *              file to get an exclusive lock (supposedly other accessors
14 *              will honor this).  Failure is handled currently by keeping
15 *              all the changes in memory, and writing it out when it is
16 *              closed.  If the system crashes before this happens, the old
17 *              version will still be around.  If the system crashes after
18 *              this happens, the new version will be around. 
19 *
20 *              This is banking on the fact that system crashes during the
21 *              atomic close is not likely.  If this is an incorrect
22 *              assumption, then this can be recoded to update a new version
23 *              of the file, and 'mv' it over.  Or we could play with shadow
24 *              blocks or something else.
25 *
26 */
27
28
29#include "../include/atom.h"
30#include <sys/types.h>
31#include <sys/stat.h>
32#include <sys/file.h>
33#include <unistd.h>
34#if HAVE_FCNTL_H
35#include <fcntl.h>
36#endif
37#define NULL 0
38#define max(a, b) (a > b ? a : b)
39#define min(a, b) (a < b ? a : b)
40
41
42/* chunk size of atomic blocks */
43#define ABLOCKSIZE 512
44#define ABLKSINDIR 24
45
46typedef unsigned short block_num;
47
48struct dir_blk {
49     struct dir_blk *next;
50     short used;
51     char *bptr [ABLKSINDIR];
52     block_num bnum [ABLKSINDIR];
53};
54
55
56char *malloc(),*calloc();
57char *find_block();
58
59static int maxdirty = 0;                                /* meter: maximum dirty blks */
60
61/*
62 *
63 * aopen --     opens up an atomic file, returning an AFILE back to the caller.
64 *              The caller gives a UNIX file number that he wants to go
65 *              nuclear.
66 *
67 */
68afile aopen (d)
69int d;
70{
71     afile af;
72     struct stat buf;
73     struct flock lock;
74
75    lock.l_type = F_WRLCK;
76    lock.l_start = 0;
77    lock.l_whence = 0;
78    lock.l_len = 0;
79    if (fcntl(d, F_SETLKW, &lock))
80          return (NULL);
81     if (fstat (d, &buf) < 0)
82          goto punt;
83
84     af = (afile) malloc (sizeof (*af));
85     if (af == NULL)
86          goto punt;                            /* no memory, ack! */
87
88     af -> desc = d;
89     af -> dir_list = NULL;
90     af -> dirty_blks = 0;                      /* no dirty blocks yet */
91     af -> file_size = buf.st_size;
92
93     return (af);
94
95punt:
96     lock.l_type = F_UNLCK;
97     lock.l_start = 0;
98      lock.l_whence = 0;
99      lock.l_len = 0;
100     fcntl(d, F_SETLK, &lock);
101     return (NULL);
102}
103
104/*
105 *
106 * aread --     Atomic read.  Reads from an atomic file.  Pos is the place to
107 *              lseek to.   This checks to see if the desired data is in
108 *              memory, and if so, reads it.  Otherwise it simply reads from
109 *              the file.
110 *
111 */
112aread(af,buf,nbytes,pos)
113afile af;
114int nbytes, pos;
115char *buf;
116{
117     int hi, numleft, toread, offset;
118     char *bptr,*dest_ptr;
119     block_num bstart,bend,bn;
120     int result;
121
122     hi = pos + nbytes - 1;                             /* high end of range */
123     bstart = pos / ABLOCKSIZE;
124     bend = hi / ABLOCKSIZE;
125     offset = pos % ABLOCKSIZE;                         /* offset of blk we're playing with */
126
127     numleft = nbytes;
128     dest_ptr = buf;                                    /* caller's buffer */
129
130     for (bn = bstart; bn <= bend; bn++) {
131          toread = min (numleft, ABLOCKSIZE - offset);
132          if ((bptr = find_block (af, bn)) == NULL) {   /* not there, read file */
133               lseek (af -> desc, (long)(bn * ABLOCKSIZE + offset), SEEK_SET);
134               result = read (af -> desc, dest_ptr, toread);
135               if (result != toread)
136                    goto read_error;
137          } else {
138               memcpy (dest_ptr, bptr + offset,  toread);
139          }
140          dest_ptr += toread;
141          numleft -= toread;
142          offset = 0;                                   /* start from blk beginning next time */
143     }
144
145     return (nbytes);
146
147read_error:
148     return (result);
149}
150
151/*
152 *
153 * awrite () -- Routine to handle atomic writing of the file.  This
154 *              hairy beast allocates blocks when needed.
155 *
156 */
157awrite(af,buf,nbytes,pos)
158afile af;
159int nbytes, pos;
160char *buf;
161{
162     int hi, numleft, towrite, offset;
163     char *bptr,*src_ptr;
164     block_num bstart,bend,bn;
165     int result;
166     
167     hi = pos + nbytes - 1;                             /* high end of range */
168     bstart = pos / ABLOCKSIZE;
169     bend = hi / ABLOCKSIZE;
170     offset = pos % ABLOCKSIZE;
171     
172     numleft = nbytes;
173     src_ptr = buf;                                     /* caller's buffer */
174     for (bn = bstart; bn <= bend; bn++) {
175          towrite = min (ABLOCKSIZE - offset, numleft);
176          if ((bptr = find_block (af, bn)) == NULL) {   /* not there, make new one */
177               bptr = calloc (1, ABLOCKSIZE);
178               lseek (af -> desc, (long)(bn * ABLOCKSIZE), SEEK_SET);
179               result = read (af -> desc, bptr, ABLOCKSIZE);
180               if (result < 0)
181                    goto write_error;
182               /* write block back out, thus reserving quota */
183               lseek (af -> desc, (long)(bn * ABLOCKSIZE), SEEK_SET);
184               result = write (af -> desc, bptr, ABLOCKSIZE);
185               if (result < 0)
186                    goto write_error;
187
188               add_block (af, bn, bptr);
189          }
190          memcpy (bptr+offset, src_ptr, towrite);
191          src_ptr += towrite;
192          numleft -= towrite;
193          offset = 0;                                   /* after first, no offset */
194     }
195     return (nbytes);
196
197write_error:
198     return (result);
199}
200
201/*
202 *
203 * aclose --    Atomic closes the file.  This is where the action is.  Our
204 *              changes are in memory, and we have to write it all back out.
205 *
206 *
207 */
208aclose(af)
209afile af;
210{
211     struct dir_blk *db,*olddb;
212     register i;
213    struct flock lock;
214
215     /* loop thru dir blocks, writing all blocks */
216     for (db = (struct dir_blk *) af -> dir_list; db != NULL;) {
217          for (i = 0; i < db -> used; i++) {
218               lseek (af -> desc, (long)(db -> bnum[i] * ABLOCKSIZE),
219                      SEEK_SET);
220               write (af -> desc, db -> bptr[i], ABLOCKSIZE);
221               free (db -> bptr[i]);
222               db -> bptr[i] = 0;
223          }
224          olddb = db;
225          db = db -> next;
226          free((char *)olddb);
227     }
228
229     fsync(af -> desc);                         /* tell kernel to get a move on */
230     lock.l_type = F_UNLCK;
231     lock.l_start = 0;
232     lock.l_whence = 0;
233     lock.l_len = 0;
234     fcntl(af -> desc, F_SETLK, &lock);
235     af -> dir_list = NULL;
236     maxdirty = max(maxdirty, af -> dirty_blks);
237     af -> desc = -1;                           /* to prevent reuse */
238     (void) free ((char *)af);
239}
240
241/*
242 *
243 * aabort --    Routine to close an atomic file, without writing the changes
244 *              out.
245 *
246 */
247
248aabort(af)
249afile af;
250{
251     struct dir_blk *db,*olddb;
252     register i;
253     struct flock lock;
254
255
256     /* loop thru dir blocks, freeing all blocks */
257     for (db = (struct dir_blk *) af -> dir_list; db != NULL;) {
258          for (i = 0; i < db -> used; i++) {
259                  (void) free (db -> bptr[i]);
260                  db -> bptr[i] = 0;
261          }
262          olddb = db;
263          db = db -> next;
264          (void) free((char *)olddb);
265     }
266
267     ftruncate(af -> desc, (long)(af -> file_size));
268     lock.l_type = F_UNLCK;
269     lock.l_start = 0;
270     lock.l_whence = 0;
271     lock.l_len = 0;
272     fcntl(af -> desc, F_SETLK, &lock);
273
274     af -> dir_list = NULL;
275     maxdirty = max(maxdirty, af -> dirty_blks);
276     af -> desc = -1;                           /* to prevent reuse */
277     (void) free ((char *)af);
278}
279
280
281/*
282 *
283 * find_block -- Checks the block list for the given block, and returns
284 *               a pointer to the block.
285 *
286 */
287
288char *find_block(af, bnum)
289afile af;
290block_num bnum;
291{
292     int i;
293     struct dir_blk *db;
294
295     if (af -> dir_list != NULL) {                      /* anything there? */
296          for (db = (struct dir_blk *) af -> dir_list; db != NULL; db = db -> next) {
297               for (i = 0; i < db -> used; i++)
298                    if (bnum == db -> bnum [i])         /* found it */
299                         return (db -> bptr [i]);
300          }
301     }
302
303     return (NULL);
304}
305
306/*
307 *
308 * add_block -- Add a new block to the dir structure.
309 *
310 */
311add_block (af, bnum, bptr)
312afile af;
313block_num bnum;
314char *bptr;
315{
316     struct dir_blk *db;
317
318     if (af -> dir_list == NULL) {                      /* create dir list */
319          af -> dir_list = calloc (1, sizeof (struct dir_blk));
320          /* calloc initializes everything we need... */
321     }
322
323     db = (struct dir_blk *) af -> dir_list;
324     if (db -> used >= ABLKSINDIR) {                    /* no space, make new */
325          db = (struct dir_blk *) calloc (1, sizeof (struct dir_blk));
326          db -> next = (struct dir_blk *) af -> dir_list;
327          af -> dir_list = (char *) db;
328          /* fall thru to add into db (which has space now) */
329     }
330
331     db -> bptr [db -> used] = bptr;
332     db -> bnum [db -> used] = bnum;
333     db -> used++;
334
335     af -> dirty_blks++;
336
337     return;
338}
339
Note: See TracBrowser for help on using the repository browser.