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

Revision 22404, 8.1 KB checked in by ghudson, 19 years ago (diff)
Eliminate declarations of system functions which cause warnings or errors. Fix some broken ss library calls.
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 max(a, b) (a > b ? a : b)
38#define min(a, b) (a < b ? a : b)
39
40
41/* chunk size of atomic blocks */
42#define ABLOCKSIZE 512
43#define ABLKSINDIR 24
44
45typedef unsigned short block_num;
46
47struct dir_blk {
48     struct dir_blk *next;
49     short used;
50     char *bptr [ABLKSINDIR];
51     block_num bnum [ABLKSINDIR];
52};
53
54
55char *find_block();
56
57static int maxdirty = 0;                                /* meter: maximum dirty blks */
58
59/*
60 *
61 * aopen --     opens up an atomic file, returning an AFILE back to the caller.
62 *              The caller gives a UNIX file number that he wants to go
63 *              nuclear.
64 *
65 */
66afile aopen (d)
67int d;
68{
69     afile af;
70     struct stat buf;
71     struct flock lock;
72
73    lock.l_type = F_WRLCK;
74    lock.l_start = 0;
75    lock.l_whence = 0;
76    lock.l_len = 0;
77    if (fcntl(d, F_SETLKW, &lock))
78          return (NULL);
79     if (fstat (d, &buf) < 0)
80          goto punt;
81
82     af = (afile) malloc (sizeof (*af));
83     if (af == NULL)
84          goto punt;                            /* no memory, ack! */
85
86     af -> desc = d;
87     af -> dir_list = NULL;
88     af -> dirty_blks = 0;                      /* no dirty blocks yet */
89     af -> file_size = buf.st_size;
90
91     return (af);
92
93punt:
94     lock.l_type = F_UNLCK;
95     lock.l_start = 0;
96      lock.l_whence = 0;
97      lock.l_len = 0;
98     fcntl(d, F_SETLK, &lock);
99     return (NULL);
100}
101
102/*
103 *
104 * aread --     Atomic read.  Reads from an atomic file.  Pos is the place to
105 *              lseek to.   This checks to see if the desired data is in
106 *              memory, and if so, reads it.  Otherwise it simply reads from
107 *              the file.
108 *
109 */
110aread(af,buf,nbytes,pos)
111afile af;
112int nbytes, pos;
113char *buf;
114{
115     int hi, numleft, toread, offset;
116     char *bptr,*dest_ptr;
117     block_num bstart,bend,bn;
118     int result;
119
120     hi = pos + nbytes - 1;                             /* high end of range */
121     bstart = pos / ABLOCKSIZE;
122     bend = hi / ABLOCKSIZE;
123     offset = pos % ABLOCKSIZE;                         /* offset of blk we're playing with */
124
125     numleft = nbytes;
126     dest_ptr = buf;                                    /* caller's buffer */
127
128     for (bn = bstart; bn <= bend; bn++) {
129          toread = min (numleft, ABLOCKSIZE - offset);
130          if ((bptr = find_block (af, bn)) == NULL) {   /* not there, read file */
131               lseek (af -> desc, (long)(bn * ABLOCKSIZE + offset), SEEK_SET);
132               result = read (af -> desc, dest_ptr, toread);
133               if (result != toread)
134                    goto read_error;
135          } else {
136               memcpy (dest_ptr, bptr + offset,  toread);
137          }
138          dest_ptr += toread;
139          numleft -= toread;
140          offset = 0;                                   /* start from blk beginning next time */
141     }
142
143     return (nbytes);
144
145read_error:
146     return (result);
147}
148
149/*
150 *
151 * awrite () -- Routine to handle atomic writing of the file.  This
152 *              hairy beast allocates blocks when needed.
153 *
154 */
155awrite(af,buf,nbytes,pos)
156afile af;
157int nbytes, pos;
158char *buf;
159{
160     int hi, numleft, towrite, offset;
161     char *bptr,*src_ptr;
162     block_num bstart,bend,bn;
163     int result;
164     
165     hi = pos + nbytes - 1;                             /* high end of range */
166     bstart = pos / ABLOCKSIZE;
167     bend = hi / ABLOCKSIZE;
168     offset = pos % ABLOCKSIZE;
169     
170     numleft = nbytes;
171     src_ptr = buf;                                     /* caller's buffer */
172     for (bn = bstart; bn <= bend; bn++) {
173          towrite = min (ABLOCKSIZE - offset, numleft);
174          if ((bptr = find_block (af, bn)) == NULL) {   /* not there, make new one */
175               bptr = calloc (1, ABLOCKSIZE);
176               lseek (af -> desc, (long)(bn * ABLOCKSIZE), SEEK_SET);
177               result = read (af -> desc, bptr, ABLOCKSIZE);
178               if (result < 0)
179                    goto write_error;
180               /* write block back out, thus reserving quota */
181               lseek (af -> desc, (long)(bn * ABLOCKSIZE), SEEK_SET);
182               result = write (af -> desc, bptr, ABLOCKSIZE);
183               if (result < 0)
184                    goto write_error;
185
186               add_block (af, bn, bptr);
187          }
188          memcpy (bptr+offset, src_ptr, towrite);
189          src_ptr += towrite;
190          numleft -= towrite;
191          offset = 0;                                   /* after first, no offset */
192     }
193     return (nbytes);
194
195write_error:
196     return (result);
197}
198
199/*
200 *
201 * aclose --    Atomic closes the file.  This is where the action is.  Our
202 *              changes are in memory, and we have to write it all back out.
203 *
204 *
205 */
206aclose(af)
207afile af;
208{
209     struct dir_blk *db,*olddb;
210     register i;
211    struct flock lock;
212
213     /* loop thru dir blocks, writing all blocks */
214     for (db = (struct dir_blk *) af -> dir_list; db != NULL;) {
215          for (i = 0; i < db -> used; i++) {
216               lseek (af -> desc, (long)(db -> bnum[i] * ABLOCKSIZE),
217                      SEEK_SET);
218               write (af -> desc, db -> bptr[i], ABLOCKSIZE);
219               free (db -> bptr[i]);
220               db -> bptr[i] = 0;
221          }
222          olddb = db;
223          db = db -> next;
224          free((char *)olddb);
225     }
226
227     fsync(af -> desc);                         /* tell kernel to get a move on */
228     lock.l_type = F_UNLCK;
229     lock.l_start = 0;
230     lock.l_whence = 0;
231     lock.l_len = 0;
232     fcntl(af -> desc, F_SETLK, &lock);
233     af -> dir_list = NULL;
234     maxdirty = max(maxdirty, af -> dirty_blks);
235     af -> desc = -1;                           /* to prevent reuse */
236     (void) free ((char *)af);
237}
238
239/*
240 *
241 * aabort --    Routine to close an atomic file, without writing the changes
242 *              out.
243 *
244 */
245
246aabort(af)
247afile af;
248{
249     struct dir_blk *db,*olddb;
250     register i;
251     struct flock lock;
252
253
254     /* loop thru dir blocks, freeing all blocks */
255     for (db = (struct dir_blk *) af -> dir_list; db != NULL;) {
256          for (i = 0; i < db -> used; i++) {
257                  (void) free (db -> bptr[i]);
258                  db -> bptr[i] = 0;
259          }
260          olddb = db;
261          db = db -> next;
262          (void) free((char *)olddb);
263     }
264
265     ftruncate(af -> desc, (long)(af -> file_size));
266     lock.l_type = F_UNLCK;
267     lock.l_start = 0;
268     lock.l_whence = 0;
269     lock.l_len = 0;
270     fcntl(af -> desc, F_SETLK, &lock);
271
272     af -> dir_list = NULL;
273     maxdirty = max(maxdirty, af -> dirty_blks);
274     af -> desc = -1;                           /* to prevent reuse */
275     (void) free ((char *)af);
276}
277
278
279/*
280 *
281 * find_block -- Checks the block list for the given block, and returns
282 *               a pointer to the block.
283 *
284 */
285
286char *find_block(af, bnum)
287afile af;
288block_num bnum;
289{
290     int i;
291     struct dir_blk *db;
292
293     if (af -> dir_list != NULL) {                      /* anything there? */
294          for (db = (struct dir_blk *) af -> dir_list; db != NULL; db = db -> next) {
295               for (i = 0; i < db -> used; i++)
296                    if (bnum == db -> bnum [i])         /* found it */
297                         return (db -> bptr [i]);
298          }
299     }
300
301     return (NULL);
302}
303
304/*
305 *
306 * add_block -- Add a new block to the dir structure.
307 *
308 */
309add_block (af, bnum, bptr)
310afile af;
311block_num bnum;
312char *bptr;
313{
314     struct dir_blk *db;
315
316     if (af -> dir_list == NULL) {                      /* create dir list */
317          af -> dir_list = calloc (1, sizeof (struct dir_blk));
318          /* calloc initializes everything we need... */
319     }
320
321     db = (struct dir_blk *) af -> dir_list;
322     if (db -> used >= ABLKSINDIR) {                    /* no space, make new */
323          db = (struct dir_blk *) calloc (1, sizeof (struct dir_blk));
324          db -> next = (struct dir_blk *) af -> dir_list;
325          af -> dir_list = (char *) db;
326          /* fall thru to add into db (which has space now) */
327     }
328
329     db -> bptr [db -> used] = bptr;
330     db -> bnum [db -> used] = bnum;
331     db -> used++;
332
333     af -> dirty_blks++;
334
335     return;
336}
337
Note: See TracBrowser for help on using the repository browser.