int pid_write(const char* fn) { if (!fn) return KNOT_EINVAL; /* Convert. */ char buf[64]; int len = 0; len = snprintf(buf, sizeof(buf), "%lu", (unsigned long) getpid()); if (len < 0) return KNOT_EINVAL; /* Create file. */ int ret = KNOT_EOK; int fd = open(fn, O_RDWR|O_CREAT, 0644); if (fd >= 0) { if (write(fd, buf, len) != len) ret = KNOT_ERROR; close(fd); } else { ret = knot_map_errno(errno); } return ret; }
int dt_unit_unlock(dt_unit_t *unit) { // Check input if (unit == 0) { return KNOT_EINVAL; } int ret = pthread_mutex_unlock(&unit->_mx); /* Map errors. */ if (ret < 0) { return knot_map_errno(EINVAL, EAGAIN); } return KNOT_EOK; }
int journal_create(const char *fn, uint16_t max_nodes) { if (fn == NULL) { return KNOT_EINVAL; } /* File lock. */ struct flock fl; memset(&fl, 0, sizeof(struct flock)); fl.l_type = F_WRLCK; fl.l_whence = SEEK_SET; fl.l_start = 0; fl.l_len = 0; fl.l_pid = getpid(); /* Create journal file. */ int fd = open(fn, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP); if (fd < 0) { dbg_journal("journal: failed to create file '%s'\n", fn); return knot_map_errno(errno); } /* Lock. */ fcntl(fd, F_SETLKW, &fl); fl.l_type = F_UNLCK; /* Create journal header. */ dbg_journal("journal: creating header\n"); const char magic[MAGIC_LENGTH] = JOURNAL_MAGIC; if (!sfwrite(magic, MAGIC_LENGTH, fd)) { fcntl(fd, F_SETLK, &fl); close(fd); remove(fn); return KNOT_ERROR; } crc_t crc = crc_init(); if (!sfwrite(&crc, sizeof(crc_t), fd)) { fcntl(fd, F_SETLK, &fl); close(fd); remove(fn); return KNOT_ERROR; } if (!sfwrite(&max_nodes, sizeof(uint16_t), fd)) { fcntl(fd, F_SETLK, &fl); close(fd); remove(fn); return KNOT_ERROR; } /* Create node queue head + tail. * qhead points to least recent node * qtail points to next free node * qhead == qtail means empty queue */ uint16_t zval = 0; if (!sfwrite(&zval, sizeof(uint16_t), fd)) { fcntl(fd, F_SETLK, &fl); close(fd); remove(fn); return KNOT_ERROR; } if (!sfwrite(&zval, sizeof(uint16_t), fd)) { fcntl(fd, F_SETLK, &fl); close(fd); remove(fn); return KNOT_ERROR; } dbg_journal_verb("journal: creating free segment descriptor\n"); /* Create free segment descriptor. */ journal_node_t jn; memset(&jn, 0, sizeof(journal_node_t)); jn.id = 0; jn.flags = JOURNAL_VALID; jn.pos = JOURNAL_HSIZE + (max_nodes + 1) * sizeof(journal_node_t); jn.len = 0; if (!sfwrite(&jn, sizeof(journal_node_t), fd)) { fcntl(fd, F_SETLK, &fl); close(fd); remove(fn); return KNOT_ERROR; } /* Create nodes. */ dbg_journal("journal: creating node table, size=%u\n", max_nodes); memset(&jn, 0, sizeof(journal_node_t)); for(uint16_t i = 0; i < max_nodes; ++i) { if (!sfwrite(&jn, sizeof(journal_node_t), fd)) { fcntl(fd, F_SETLK, &fl); close(fd); if (remove(fn) < 0) { dbg_journal("journal: failed to remove journal file after error\n"); } return KNOT_ERROR; } } /* Recalculate CRC. */ if (journal_update_crc(fd) != KNOT_EOK) { fcntl(fd, F_SETLK, &fl); close(fd); if(remove(fn) < 0) { dbg_journal("journal: failed to remove journal file after error\n"); } return KNOT_ERROR; } /* Unlock and close. */ fcntl(fd, F_SETLK, &fl); close(fd); /* Journal file created. */ dbg_journal("journal: file '%s' initialized\n", fn); return KNOT_EOK; }
/*! \brief Open journal file for r/w (returns error if not exists). */ static int journal_open_file(journal_t *j) { assert(j != NULL); int ret = KNOT_EOK; j->fd = open(j->path, O_RDWR); dbg_journal_verb("journal: open_file '%s'\n", j->path); if (j->fd < 0) { if (errno != ENOENT) { return knot_map_errno(errno); } /* Create new journal file and open if not exists. */ ret = journal_create(j->path, JOURNAL_NCOUNT); if(ret == KNOT_EOK) { return journal_open_file(j); } return ret; } /* File lock. */ memset(&j->fl, 0, sizeof(struct flock)); j->fl.l_type = F_WRLCK; j->fl.l_whence = SEEK_SET; j->fl.l_start = 0; j->fl.l_len = 0; j->fl.l_pid = getpid(); /* Attempt to lock. */ dbg_journal_verb("journal: locking journal %s\n", j->path); ret = fcntl(j->fd, F_SETLK, &j->fl); /* Lock. */ if (ret < 0) { struct flock efl = {0}; memcpy(&efl, &j->fl, sizeof(struct flock)); (void) fcntl(j->fd, F_GETLK, &efl); log_server_warning("Journal file '%s' is locked by process " "PID=%d, waiting for process to " "release lock.\n", j->path, efl.l_pid); ret = fcntl(j->fd, F_SETLKW, &j->fl); } UNUSED(ret); dbg_journal("journal: locked journal %s (returned %d)\n", j->path, ret); /* Read magic bytes. */ dbg_journal("journal: reading magic bytes\n"); const char magic_req[MAGIC_LENGTH] = JOURNAL_MAGIC; char magic[MAGIC_LENGTH]; if (!sfread(magic, MAGIC_LENGTH, j->fd)) { dbg_journal_verb("journal: cannot read magic bytes\n"); goto open_file_error; } if (memcmp(magic, magic_req, MAGIC_LENGTH) != 0) { log_server_warning("Journal file '%s' version is too old, " "it will be purged.\n", j->path); close(j->fd); j->fd = -1; ret = journal_create(j->path, JOURNAL_NCOUNT); if(ret == KNOT_EOK) { return journal_open_file(j); } return ret; } crc_t crc = 0; if (!sfread(&crc, sizeof(crc_t), j->fd)) { dbg_journal_verb("journal: cannot read CRC\n"); goto open_file_error; } /* Recalculate CRC. */ char buf[4096]; ssize_t rb = 0; crc_t crc_calc = crc_init(); while((rb = read(j->fd, buf, sizeof(buf))) > 0) { crc_calc = crc_update(crc_calc, (const unsigned char *)buf, rb); } /* Compare */ if (crc == crc_calc) { /* Rewind. */ if (lseek(j->fd, MAGIC_LENGTH + sizeof(crc_t), SEEK_SET) < 0) { goto open_file_error; } } else { log_server_warning("Journal file '%s' CRC error, " "it will be purged.\n", j->path); close(j->fd); j->fd = -1; ret = journal_create(j->path, JOURNAL_NCOUNT); if(ret == KNOT_EOK) { return journal_open_file(j); } return ret; } /* Get journal file size. */ struct stat st; if (stat(j->path, &st) < 0) { dbg_journal_verb("journal: cannot get journal fsize\n"); goto open_file_error; } /* Set file size. */ j->fsize = st.st_size; /* Read maximum number of entries. */ if (!sfread(&j->max_nodes, sizeof(uint16_t), j->fd)) { dbg_journal_verb("journal: cannot read max_nodes\n"); goto open_file_error; } /* Check max_nodes, but this is riddiculous. */ if (j->max_nodes == 0) { dbg_journal_verb("journal: invalid max_nodes\n"); goto open_file_error; } /* Allocate nodes. */ const size_t node_len = sizeof(journal_node_t); j->nodes = malloc(j->max_nodes * node_len); if (j->nodes == NULL) { dbg_journal_verb("journal: can't allocate nodes\n"); goto open_file_error; } else { memset(j->nodes, 0, j->max_nodes * node_len); } /* Load node queue state. */ j->qhead = j->qtail = 0; if (!sfread(&j->qhead, sizeof(uint16_t), j->fd)) { dbg_journal_verb("journal: cannot read qhead\n"); goto open_file_error; } /* Load queue tail. */ if (!sfread(&j->qtail, sizeof(uint16_t), j->fd)) { dbg_journal_verb("journal: cannot read qtail\n"); goto open_file_error; } /* Check head + tail */ if (j->qtail >= j->max_nodes || j->qhead >= j->max_nodes) { dbg_journal_verb("journal: queue pointers corrupted\n"); goto open_file_error; } /* Load empty segment descriptor. */ if (!sfread(&j->free, node_len, j->fd)) { dbg_journal_verb("journal: cannot read free segment ptr\n"); goto open_file_error; } /* Read journal descriptors table. */ if (!sfread(j->nodes, j->max_nodes * node_len, j->fd)) { dbg_journal_verb("journal: cannot read node table\n"); goto open_file_error; } dbg_journal("journal: opened journal size=%u, queue=<%u, %u>, fd=%d\n", j->max_nodes, j->qhead, j->qtail, j->fd); /* Check node queue. */ unsigned qtail_free = (jnode_flags(j, j->qtail) <= JOURNAL_FREE); unsigned qhead_free = j->max_nodes - 1; /* Left of qhead must be free.*/ if (j->qhead > 0) { qhead_free = (j->qhead - 1); } qhead_free = (jnode_flags(j, qhead_free) <= JOURNAL_FREE); if ((j->qhead != j->qtail) && (!qtail_free || !qhead_free)) { log_server_warning("Recovering journal '%s' metadata " "after crash.\n", j->path); ret = journal_recover(j); if (ret != KNOT_EOK) { log_server_error("Journal file '%s' is unrecoverable, " "metadata corrupted - %s\n", j->path, knot_strerror(ret)); goto open_file_error; } } /* Save file lock and return. */ return KNOT_EOK; /* Unlock and close file and return error. */ open_file_error: free(j->nodes); j->nodes = NULL; close(j->fd); j->fd = -1; return KNOT_ERROR; }