[168] | 1 | /* |
---|
| 2 | * |
---|
[1939] | 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 | * |
---|
[168] | 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> |
---|
[365] | 32 | #include <sys/file.h> |
---|
[17452] | 33 | #include <unistd.h> |
---|
[12439] | 34 | #if HAVE_FCNTL_H |
---|
[7187] | 35 | #include <fcntl.h> |
---|
[6571] | 36 | #endif |
---|
[168] | 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 | |
---|
| 45 | typedef unsigned short block_num; |
---|
| 46 | |
---|
[344] | 47 | struct dir_blk { |
---|
[168] | 48 | struct dir_blk *next; |
---|
| 49 | short used; |
---|
| 50 | char *bptr [ABLKSINDIR]; |
---|
| 51 | block_num bnum [ABLKSINDIR]; |
---|
| 52 | }; |
---|
| 53 | |
---|
| 54 | |
---|
| 55 | char *find_block(); |
---|
| 56 | |
---|
| 57 | static 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 | */ |
---|
| 66 | afile aopen (d) |
---|
| 67 | int d; |
---|
| 68 | { |
---|
| 69 | afile af; |
---|
| 70 | struct stat buf; |
---|
[7187] | 71 | struct flock lock; |
---|
[168] | 72 | |
---|
[7187] | 73 | lock.l_type = F_WRLCK; |
---|
| 74 | lock.l_start = 0; |
---|
| 75 | lock.l_whence = 0; |
---|
| 76 | lock.l_len = 0; |
---|
[9551] | 77 | if (fcntl(d, F_SETLKW, &lock)) |
---|
[7187] | 78 | return (NULL); |
---|
[364] | 79 | if (fstat (d, &buf) < 0) |
---|
| 80 | goto punt; |
---|
| 81 | |
---|
[168] | 82 | af = (afile) malloc (sizeof (*af)); |
---|
| 83 | if (af == NULL) |
---|
[364] | 84 | goto punt; /* no memory, ack! */ |
---|
[168] | 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); |
---|
[364] | 92 | |
---|
| 93 | punt: |
---|
[7187] | 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); |
---|
[364] | 99 | return (NULL); |
---|
[168] | 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 | */ |
---|
| 110 | aread(af,buf,nbytes,pos) |
---|
| 111 | afile af; |
---|
| 112 | int nbytes, pos; |
---|
| 113 | char *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 */ |
---|
[17452] | 131 | lseek (af -> desc, (long)(bn * ABLOCKSIZE + offset), SEEK_SET); |
---|
[168] | 132 | result = read (af -> desc, dest_ptr, toread); |
---|
| 133 | if (result != toread) |
---|
| 134 | goto read_error; |
---|
| 135 | } else { |
---|
[8855] | 136 | memcpy (dest_ptr, bptr + offset, toread); |
---|
[168] | 137 | } |
---|
| 138 | dest_ptr += toread; |
---|
| 139 | numleft -= toread; |
---|
| 140 | offset = 0; /* start from blk beginning next time */ |
---|
| 141 | } |
---|
| 142 | |
---|
| 143 | return (nbytes); |
---|
| 144 | |
---|
| 145 | read_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 | */ |
---|
| 155 | awrite(af,buf,nbytes,pos) |
---|
| 156 | afile af; |
---|
| 157 | int nbytes, pos; |
---|
| 158 | char *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); |
---|
[17452] | 176 | lseek (af -> desc, (long)(bn * ABLOCKSIZE), SEEK_SET); |
---|
[168] | 177 | result = read (af -> desc, bptr, ABLOCKSIZE); |
---|
| 178 | if (result < 0) |
---|
| 179 | goto write_error; |
---|
| 180 | /* write block back out, thus reserving quota */ |
---|
[17452] | 181 | lseek (af -> desc, (long)(bn * ABLOCKSIZE), SEEK_SET); |
---|
[168] | 182 | result = write (af -> desc, bptr, ABLOCKSIZE); |
---|
| 183 | if (result < 0) |
---|
| 184 | goto write_error; |
---|
| 185 | |
---|
| 186 | add_block (af, bn, bptr); |
---|
| 187 | } |
---|
[8855] | 188 | memcpy (bptr+offset, src_ptr, towrite); |
---|
[168] | 189 | src_ptr += towrite; |
---|
| 190 | numleft -= towrite; |
---|
| 191 | offset = 0; /* after first, no offset */ |
---|
| 192 | } |
---|
| 193 | return (nbytes); |
---|
| 194 | |
---|
| 195 | write_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 | */ |
---|
| 206 | aclose(af) |
---|
| 207 | afile af; |
---|
| 208 | { |
---|
| 209 | struct dir_blk *db,*olddb; |
---|
| 210 | register i; |
---|
[7187] | 211 | struct flock lock; |
---|
[168] | 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++) { |
---|
[17452] | 216 | lseek (af -> desc, (long)(db -> bnum[i] * ABLOCKSIZE), |
---|
| 217 | SEEK_SET); |
---|
[168] | 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 */ |
---|
[7187] | 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); |
---|
[168] | 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 | |
---|
| 246 | aabort(af) |
---|
| 247 | afile af; |
---|
| 248 | { |
---|
| 249 | struct dir_blk *db,*olddb; |
---|
| 250 | register i; |
---|
[12439] | 251 | struct flock lock; |
---|
[168] | 252 | |
---|
[7187] | 253 | |
---|
[168] | 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)); |
---|
[7187] | 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); |
---|
[168] | 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 | |
---|
| 286 | char *find_block(af, bnum) |
---|
| 287 | afile af; |
---|
| 288 | block_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 | */ |
---|
| 309 | add_block (af, bnum, bptr) |
---|
| 310 | afile af; |
---|
| 311 | block_num bnum; |
---|
| 312 | char *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 | |
---|