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 read(md->lfd, tbuf, sizeof(tbuf) - 1);
00112 pid = strtol(tbuf, NULL, 10);
00113 if (pid == (int)getpid())
00114 goto Bail;
00115 if (fcntl(md->lfd, F_SETLK, &flk) == -1) {
00116 if (errno == EACCES || errno == EAGAIN) {
00117 fcntl(md->lfd, F_GETLK, &flk);
00118 if (msgs != NULL)
00119 ccn_charbuf_putf(msgs, "Locked by process id %d. ", flk.l_pid);
00120 goto Bail;
00121 }
00122 }
00123 if (msgs != NULL)
00124 ccn_charbuf_putf(msgs, "Breaking stale lock by pid %d. ", pid);
00125 lseek(md->lfd, 0, SEEK_SET);
00126 ftruncate(md->lfd, 0);
00127 }
00128 else {
00129 if (msgs != NULL)
00130 ccn_charbuf_append_string(msgs, "Unable to open pid file. ");
00131 goto Bail;
00132 }
00133 }
00134 else if (fcntl(md->lfd, F_SETLK, &flk) == -1) {
00135 if (errno == EACCES || errno == EAGAIN) {
00136 fcntl(md->lfd, F_GETLK, &flk);
00137 if (msgs != NULL)
00138 ccn_charbuf_putf(msgs, "Locked by process id %d. ", flk.l_pid);
00139 goto Bail;
00140 }
00141 }
00142
00143 temp->length = 0;
00144 ccn_charbuf_putf(temp, "%d", (int)getpid());
00145 if (write(md->lfd, temp->buf, temp->length) < 0) {
00146 if (msgs != NULL)
00147 ccn_charbuf_append_string(msgs, "Unable to write pid file.");
00148 goto Bail;
00149 }
00150
00151
00152 temp->length = 0;
00153 ccn_charbuf_append_charbuf(temp, md->dirpath);
00154 ccn_charbuf_putf(temp, "/maxnodeid");
00155 fd = open(ccn_charbuf_as_string(temp), O_RDWR);
00156 if (fd != -1) {
00157 memset(tbuf, 0, sizeof(tbuf));
00158 read(fd, tbuf, sizeof(tbuf) - 1);
00159 errno = EINVAL;
00160 maxnodeid = strtoul(tbuf, NULL, 10);
00161 if (maxnodeid == 0)
00162 goto Bail;
00163 }
00164
00165 ans = tans;
00166 tans = NULL;
00167 res = md->dirpath->length;
00168 if (res >= sizeof(ans->clue))
00169 res = sizeof(ans->clue) - 1;
00170 memcpy(ans->clue, md->dirpath->buf + md->dirpath->length - res, res);
00171 ans->btopen = &bts_open;
00172 ans->btread = &bts_read;
00173 ans->btwrite = &bts_write;
00174 ans->btclose = &bts_close;
00175 ans->btdestroy = &bts_destroy;
00176 ans->maxnodeid = maxnodeid;
00177 ans->openfds = 0;
00178 ans->data = md;
00179 md->io = ans;
00180 md = NULL;
00181 Bail:
00182 if (fd != -1) close(fd);
00183 if (tans != NULL) free(tans);
00184 if (md != NULL) {
00185 ccn_charbuf_destroy(&md->dirpath);
00186 free(md);
00187 }
00188 ccn_charbuf_destroy(&temp);
00189 return(ans);
00190 }
00191
00192 struct bts_node_state {
00193 struct ccn_btree_node *node;
00194 int fd;
00195 };
00196
00197 static int
00198 bts_open(struct ccn_btree_io *io, struct ccn_btree_node *node)
00199 {
00200 struct bts_node_state *nd = NULL;
00201 struct ccn_charbuf *temp = NULL;
00202 struct bts_data *md = io->data;
00203 int res;
00204
00205 if (node->iodata != NULL || io != md->io) abort();
00206 nd = calloc(1, sizeof(*nd));
00207 if (nd == NULL)
00208 return(-1);
00209 temp = ccn_charbuf_create();
00210 if (temp == NULL)
00211 return(-1);
00212 res = ccn_charbuf_append_charbuf(temp, md->dirpath);
00213 res |= ccn_charbuf_putf(temp, "/%u", (unsigned)node->nodeid);
00214 if (res < 0) {
00215 ccn_charbuf_destroy(&temp);
00216 free(nd);
00217 return(-1);
00218 }
00219 nd->fd = open(ccn_charbuf_as_string(temp),
00220 (O_RDWR | O_CREAT),
00221 0640);
00222 if (nd->fd != -1 && node->nodeid > io->maxnodeid) {
00223
00224 io->maxnodeid = node->nodeid;
00225 temp->length = 0;
00226 ccn_charbuf_append_charbuf(temp, md->dirpath);
00227 ccn_charbuf_putf(temp, "/maxnodeid");
00228 res = open(ccn_charbuf_as_string(temp),
00229 (O_RDWR | O_CREAT | O_TRUNC),
00230 0640);
00231 if (res >= 0) {
00232 temp->length = 0;
00233 ccn_charbuf_putf(temp, "%u", (unsigned)node->nodeid);
00234 write(res, temp->buf, temp->length);
00235 close(res);
00236 }
00237 }
00238 ccn_charbuf_destroy(&temp);
00239 if (nd->fd == -1) {
00240 free(nd);
00241 return(-1);
00242 }
00243 io->openfds++;
00244 nd->node = node;
00245 node->iodata = nd;
00246 return(nd->fd);
00247 }
00248
00249 static int
00250 bts_read(struct ccn_btree_io *io, struct ccn_btree_node *node, unsigned limit)
00251 {
00252 struct bts_node_state *nd = node->iodata;
00253 ssize_t sres;
00254 off_t offset;
00255 off_t clean = 0;
00256
00257 if (nd == NULL || nd->node != node) abort();
00258 offset = lseek(nd->fd, 0, SEEK_END);
00259 if (offset == (off_t)-1)
00260 return(-1);
00261 if (offset < limit)
00262 limit = offset;
00263 if (node->clean > 0 && node->clean <= node->buf->length)
00264 clean = node->clean;
00265 offset = lseek(nd->fd, clean, SEEK_SET);
00266 if (offset == (off_t)-1)
00267 return(-1);
00268 if (offset != clean)
00269 abort();
00270 node->buf->length = clean;
00271 sres = read(nd->fd, ccn_charbuf_reserve(node->buf, limit - clean), limit - clean);
00272 if (sres < 0)
00273 return(-1);
00274 if (sres != limit - clean) {
00275 abort();
00276 }
00277 if (sres + node->buf->length > node->buf->limit) {
00278 abort();
00279 }
00280 node->buf->length += sres;
00281 return(0);
00282 }
00283
00284 static int
00285 bts_write(struct ccn_btree_io *io, struct ccn_btree_node *node)
00286 {
00287 struct bts_node_state *nd = node->iodata;
00288 ssize_t sres;
00289 off_t offset;
00290 size_t clean = 0;
00291
00292 if (nd == NULL || nd->node != node) abort();
00293 if (node->clean > 0 && node->clean <= node->buf->length)
00294 clean = node->clean;
00295 offset = lseek(nd->fd, clean, SEEK_SET);
00296 if (offset == (off_t)-1)
00297 return(-1);
00298 if (offset != clean)
00299 abort();
00300 sres = write(nd->fd, node->buf->buf + clean, node->buf->length - clean);
00301 if (sres == -1)
00302 return(-1);
00303 if (sres + clean != node->buf->length)
00304 abort();
00305 return(ftruncate(nd->fd, node->buf->length));
00306 }
00307
00308 static int
00309 bts_close(struct ccn_btree_io *io, struct ccn_btree_node *node)
00310 {
00311 struct bts_node_state *nd = node->iodata;
00312 int res = -1;
00313
00314 if (nd != NULL && nd->node == node) {
00315 res = close(nd->fd);
00316 if (res == -1 && errno == EINTR)
00317 return(res);
00318 io->openfds--;
00319 nd->node = NULL;
00320 node->iodata = NULL;
00321 free(nd);
00322 }
00323 return(res);
00324 }
00325
00326
00327
00328
00329
00330 static int
00331 bts_remove_lockfile(struct ccn_btree_io *io)
00332 {
00333 size_t sav;
00334 int res;
00335 struct flock flk = {0};
00336 struct bts_data *md = NULL;
00337
00338 md = io->data;
00339 sav = md->dirpath->length;
00340 ccn_charbuf_putf(md->dirpath, "/.LCK");
00341 res = unlink(ccn_charbuf_as_string(md->dirpath));
00342 md->dirpath->length = sav;
00343 if (md->lfd >= 0) {
00344 flk.l_type = F_UNLCK;
00345 flk.l_whence = SEEK_SET;
00346 fcntl(md->lfd, F_SETLK, &flk);
00347 md->lfd = -1;
00348 }
00349 return(res);
00350 }
00351
00352
00353
00354
00355
00356 static int
00357 bts_destroy(struct ccn_btree_io **pio)
00358 {
00359 int res;
00360 struct bts_data *md = NULL;
00361
00362 if (*pio == NULL)
00363 return(0);
00364 if ((*pio)->btdestroy != &bts_destroy)
00365 abort();
00366 res = bts_remove_lockfile(*pio);
00367 md = (*pio)->data;
00368 if (md->io != *pio) abort();
00369 ccn_charbuf_destroy(&md->dirpath);
00370 free(md);
00371 (*pio)->data = NULL;
00372 free(*pio);
00373 *pio = NULL;
00374 return(res);
00375 }