00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <dirent.h>
00021 #include <errno.h>
00022 #include <fcntl.h>
00023 #include <sys/stat.h>
00024 #include <stdlib.h>
00025 #include <string.h>
00026 #include <signal.h>
00027 #include <unistd.h>
00028
00029 #include <ccn/btree.h>
00030 #include <ccn/charbuf.h>
00031
00032 static int bts_open(struct ccn_btree_io *, struct ccn_btree_node *);
00033 static int bts_read(struct ccn_btree_io *, struct ccn_btree_node *, unsigned);
00034 static int bts_write(struct ccn_btree_io *, struct ccn_btree_node *);
00035 static int bts_close(struct ccn_btree_io *, struct ccn_btree_node *);
00036 static int bts_destroy(struct ccn_btree_io **);
00037
00038 struct bts_data {
00039 struct ccn_btree_io *io;
00040 struct ccn_charbuf *dirpath;
00041 int lfd;
00042 };
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056 struct ccn_btree_io *
00057 ccn_btree_io_from_directory(const char *path, struct ccn_charbuf *msgs)
00058 {
00059 DIR *d = NULL;
00060 struct bts_data *md = NULL;
00061 struct ccn_btree_io *ans = NULL;
00062 struct ccn_btree_io *tans = NULL;
00063 struct ccn_charbuf *temp = NULL;
00064 char tbuf[21];
00065 int fd = -1;
00066 struct flock flk = {0};
00067 int pid, res;
00068 int maxnodeid = 0;
00069
00070
00071 d = opendir(path);
00072 if (d == NULL)
00073 goto Bail;
00074 closedir(d);
00075 d = NULL;
00076
00077
00078 md = calloc(1, sizeof(*md));
00079 if (md == NULL)
00080 goto Bail;
00081 md->lfd = -1;
00082 md->dirpath = ccn_charbuf_create();
00083 if (md->dirpath == NULL) goto Bail;
00084 res = ccn_charbuf_putf(md->dirpath, "%s", path);
00085 if (res < 0) goto Bail;
00086 tans = calloc(1, sizeof(*tans));
00087 if (tans == NULL) goto Bail;
00088
00089
00090 temp = ccn_charbuf_create();
00091 if (temp == NULL) goto Bail;
00092 res = ccn_charbuf_append_charbuf(temp, md->dirpath);
00093 if (res < 0) goto Bail;
00094 res = ccn_charbuf_putf(temp, "/.LCK");
00095 if (res < 0) goto Bail;
00096 flk.l_type = F_WRLCK;
00097 flk.l_whence = SEEK_SET;
00098 md->lfd = open(ccn_charbuf_as_string(temp),
00099 (O_RDWR | O_CREAT | O_EXCL),
00100 0600);
00101 if (md->lfd == -1) {
00102 if (errno == EEXIST) {
00103
00104 md->lfd = open(ccn_charbuf_as_string(temp), O_RDWR);
00105 if (md->lfd == -1) {
00106 if (msgs != NULL)
00107 ccn_charbuf_append_string(msgs, "Unable to open pid file for update. ");
00108 goto Bail;
00109 }
00110 memset(tbuf, 0, sizeof(tbuf));
00111 if (read(md->lfd, tbuf, sizeof(tbuf) - 1) <= 0) {
00112 if (msgs != NULL)
00113 ccn_charbuf_append_string(msgs, "Unable to read pid from pid file. ");
00114 goto Bail;
00115 }
00116 pid = strtol(tbuf, NULL, 10);
00117 if (pid == (int)getpid())
00118 goto Bail;
00119 if (fcntl(md->lfd, F_SETLK, &flk) == -1) {
00120 if (errno == EACCES || errno == EAGAIN) {
00121 fcntl(md->lfd, F_GETLK, &flk);
00122 if (msgs != NULL)
00123 ccn_charbuf_putf(msgs, "Locked by process id %d. ", flk.l_pid);
00124 goto Bail;
00125 }
00126 }
00127 if (msgs != NULL)
00128 ccn_charbuf_putf(msgs, "Breaking stale lock by pid %d. ", pid);
00129 lseek(md->lfd, 0, SEEK_SET);
00130 if (ftruncate(md->lfd, 0) < 0) {
00131 if (msgs != NULL)
00132 ccn_charbuf_append_string(msgs, "Unable to truncate pid file. ");
00133 goto Bail;
00134 };
00135 }
00136 else {
00137 if (msgs != NULL)
00138 ccn_charbuf_append_string(msgs, "Unable to open pid file. ");
00139 goto Bail;
00140 }
00141 }
00142 else if (fcntl(md->lfd, F_SETLK, &flk) == -1) {
00143 if (errno == EACCES || errno == EAGAIN) {
00144 fcntl(md->lfd, F_GETLK, &flk);
00145 if (msgs != NULL)
00146 ccn_charbuf_putf(msgs, "Locked by process id %d. ", flk.l_pid);
00147 goto Bail;
00148 }
00149 }
00150
00151 temp->length = 0;
00152 ccn_charbuf_putf(temp, "%d", (int)getpid());
00153 if (write(md->lfd, temp->buf, temp->length) <= 0) {
00154 if (msgs != NULL)
00155 ccn_charbuf_append_string(msgs, "Unable to write pid file.");
00156 goto Bail;
00157 }
00158
00159
00160 temp->length = 0;
00161 ccn_charbuf_append_charbuf(temp, md->dirpath);
00162 ccn_charbuf_putf(temp, "/maxnodeid");
00163 fd = open(ccn_charbuf_as_string(temp), O_RDWR);
00164 if (fd != -1) {
00165 memset(tbuf, 0, sizeof(tbuf));
00166 res = read(fd, tbuf, sizeof(tbuf) - 1);
00167 errno = EINVAL;
00168 maxnodeid = strtoul(tbuf, NULL, 10);
00169 if (maxnodeid == 0)
00170 goto Bail;
00171 }
00172
00173 ans = tans;
00174 tans = NULL;
00175 res = md->dirpath->length;
00176 if (res >= sizeof(ans->clue))
00177 res = sizeof(ans->clue) - 1;
00178 memcpy(ans->clue, md->dirpath->buf + md->dirpath->length - res, res);
00179 ans->btopen = &bts_open;
00180 ans->btread = &bts_read;
00181 ans->btwrite = &bts_write;
00182 ans->btclose = &bts_close;
00183 ans->btdestroy = &bts_destroy;
00184 ans->maxnodeid = maxnodeid;
00185 ans->openfds = 0;
00186 ans->data = md;
00187 md->io = ans;
00188 md = NULL;
00189 Bail:
00190 if (fd != -1) close(fd);
00191 if (tans != NULL) free(tans);
00192 if (md != NULL) {
00193 ccn_charbuf_destroy(&md->dirpath);
00194 free(md);
00195 }
00196 ccn_charbuf_destroy(&temp);
00197 return(ans);
00198 }
00199
00200 struct bts_node_state {
00201 struct ccn_btree_node *node;
00202 int fd;
00203 };
00204
00205 static int
00206 bts_open(struct ccn_btree_io *io, struct ccn_btree_node *node)
00207 {
00208 struct bts_node_state *nd = NULL;
00209 struct ccn_charbuf *temp = NULL;
00210 struct bts_data *md = io->data;
00211 int res, l;
00212
00213 if (node->iodata != NULL || io != md->io) abort();
00214 nd = calloc(1, sizeof(*nd));
00215 if (nd == NULL)
00216 return(-1);
00217 temp = ccn_charbuf_create();
00218 if (temp == NULL)
00219 goto Bail;
00220 res = ccn_charbuf_append_charbuf(temp, md->dirpath);
00221 res |= ccn_charbuf_putf(temp, "/%u", (unsigned)node->nodeid);
00222 if (res < 0)
00223 goto Bail;
00224 nd->fd = open(ccn_charbuf_as_string(temp),
00225 (O_RDWR | O_CREAT),
00226 0640);
00227 if (nd->fd < 0)
00228 goto Bail;
00229 if (node->nodeid > io->maxnodeid) {
00230
00231 io->maxnodeid = node->nodeid;
00232 temp->length = 0;
00233 ccn_charbuf_append_charbuf(temp, md->dirpath);
00234 ccn_charbuf_putf(temp, "/maxnodeid");
00235 res = open(ccn_charbuf_as_string(temp),
00236 (O_RDWR | O_CREAT | O_TRUNC),
00237 0640);
00238 if (res < 0)
00239 goto Bail;
00240 temp->length = 0;
00241 ccn_charbuf_putf(temp, "%u", (unsigned)node->nodeid);
00242 l = write(res, temp->buf, temp->length);
00243 close(res);
00244 if (l != temp->length)
00245 goto Bail;
00246 }
00247 ccn_charbuf_destroy(&temp);
00248 io->openfds++;
00249 nd->node = node;
00250 node->iodata = nd;
00251 return(nd->fd);
00252
00253 Bail:
00254 ccn_charbuf_destroy(&temp);
00255 if (nd->fd >= 0)
00256 close(nd->fd);
00257 free(nd);
00258 return(-1);
00259 }
00260
00261 static int
00262 bts_read(struct ccn_btree_io *io, struct ccn_btree_node *node, unsigned limit)
00263 {
00264 struct bts_node_state *nd = node->iodata;
00265 ssize_t sres;
00266 off_t offset;
00267 off_t clean = 0;
00268
00269 if (nd == NULL || nd->node != node) abort();
00270 offset = lseek(nd->fd, 0, SEEK_END);
00271 if (offset == (off_t)-1)
00272 return(-1);
00273 if (offset < limit)
00274 limit = offset;
00275 if (node->clean > 0 && node->clean <= node->buf->length)
00276 clean = node->clean;
00277 offset = lseek(nd->fd, clean, SEEK_SET);
00278 if (offset == (off_t)-1)
00279 return(-1);
00280 if (offset != clean)
00281 abort();
00282 node->buf->length = clean;
00283 sres = read(nd->fd, ccn_charbuf_reserve(node->buf, limit - clean), limit - clean);
00284 if (sres < 0)
00285 return(-1);
00286 if (sres != limit - clean) {
00287 abort();
00288 }
00289 if (sres + node->buf->length > node->buf->limit) {
00290 abort();
00291 }
00292 node->buf->length += sres;
00293 return(0);
00294 }
00295
00296 static int
00297 bts_write(struct ccn_btree_io *io, struct ccn_btree_node *node)
00298 {
00299 struct bts_node_state *nd = node->iodata;
00300 ssize_t sres;
00301 off_t offset;
00302 size_t clean = 0;
00303
00304 if (nd == NULL || nd->node != node) abort();
00305 if (node->clean > 0 && node->clean <= node->buf->length)
00306 clean = node->clean;
00307 offset = lseek(nd->fd, clean, SEEK_SET);
00308 if (offset == (off_t)-1)
00309 return(-1);
00310 if (offset != clean)
00311 abort();
00312 sres = write(nd->fd, node->buf->buf + clean, node->buf->length - clean);
00313 if (sres == -1)
00314 return(-1);
00315 if (sres + clean != node->buf->length)
00316 abort();
00317 return(ftruncate(nd->fd, node->buf->length));
00318 }
00319
00320 static int
00321 bts_close(struct ccn_btree_io *io, struct ccn_btree_node *node)
00322 {
00323 struct bts_node_state *nd = node->iodata;
00324 int res = -1;
00325
00326 if (nd != NULL && nd->node == node) {
00327 res = close(nd->fd);
00328 if (res == -1 && errno == EINTR)
00329 return(res);
00330 io->openfds--;
00331 nd->node = NULL;
00332 node->iodata = NULL;
00333 free(nd);
00334 }
00335 return(res);
00336 }
00337
00338
00339
00340
00341
00342 static int
00343 bts_remove_lockfile(struct ccn_btree_io *io)
00344 {
00345 size_t sav;
00346 int res;
00347 struct flock flk = {0};
00348 struct bts_data *md = NULL;
00349
00350 md = io->data;
00351 sav = md->dirpath->length;
00352 ccn_charbuf_putf(md->dirpath, "/.LCK");
00353 res = unlink(ccn_charbuf_as_string(md->dirpath));
00354 md->dirpath->length = sav;
00355 if (md->lfd >= 0) {
00356 flk.l_type = F_UNLCK;
00357 flk.l_whence = SEEK_SET;
00358 fcntl(md->lfd, F_SETLK, &flk);
00359 md->lfd = -1;
00360 }
00361 return(res);
00362 }
00363
00364
00365
00366
00367
00368 static int
00369 bts_destroy(struct ccn_btree_io **pio)
00370 {
00371 int res;
00372 struct bts_data *md = NULL;
00373
00374 if (*pio == NULL)
00375 return(0);
00376 if ((*pio)->btdestroy != &bts_destroy)
00377 abort();
00378 res = bts_remove_lockfile(*pio);
00379 md = (*pio)->data;
00380 if (md->io != *pio) abort();
00381 ccn_charbuf_destroy(&md->dirpath);
00382 free(md);
00383 (*pio)->data = NULL;
00384 free(*pio);
00385 *pio = NULL;
00386 return(res);
00387 }