Alien-Libjio
view release on metacpan or search on metacpan
libjio/libjio/trans.c view on Meta::CPAN
pthread_mutexattr_destroy(&attr);
return ts;
}
/* Free the contents of a transaction structure */
void jtrans_free(struct jtrans *ts)
{
struct operation *tmpop;
ts->fs = NULL;
while (ts->op != NULL) {
tmpop = ts->op->next;
if (ts->op->buf && ts->op->direction == D_WRITE)
free(ts->op->buf);
if (ts->op->pdata)
free(ts->op->pdata);
free(ts->op);
ts->op = tmpop;
}
pthread_mutex_destroy(&(ts->lock));
free(ts);
}
/** Lock/unlock the ranges of the file covered by the transaction. mode must
* be either F_LOCKW or F_UNLOCK. Returns 0 on success, -1 on error. */
static int lock_file_ranges(struct jtrans *ts, int mode)
{
unsigned int nops;
off_t lr, min_offset;
struct operation *op, *start_op;
if (ts->flags & J_NOLOCK)
return 0;
/* Lock/unlock always in the same order to avoid deadlocks. We will
* begin with the operation that has the smallest start offset, and go
* from there.
* Note that this is O(n^2), but n is usually (very) small, and we're
* about to do synchronous I/O, so it's not really worrying. It has a
* small optimization to help when the operations tend to be in the
* right order. */
nops = 0;
min_offset = 0;
start_op = ts->op;
while (nops < ts->numops_r + ts->numops_w) {
for (op = start_op; op != NULL; op = op->next) {
if (min_offset < op->offset)
continue;
min_offset = op->offset;
start_op = op->next;
if (mode == F_LOCKW) {
lr = plockf(ts->fs->fd, F_LOCKW, op->offset, op->len);
if (lr == -1)
goto error;
op->locked = 1;
} else if (mode == F_UNLOCK && op->locked) {
lr = plockf(ts->fs->fd, F_UNLOCK, op->offset,
op->len);
if (lr == -1)
goto error;
op->locked = 0;
}
}
nops++;
}
return 0;
error:
return -1;
}
/** Read the previous information from the disk into the given operation
* structure. Returns 0 on success, -1 on error. */
static int operation_read_prev(struct jtrans *ts, struct operation *op)
{
ssize_t rv;
op->pdata = malloc(op->len);
if (op->pdata == NULL)
return -1;
rv = spread(ts->fs->fd, op->pdata, op->len,
op->offset);
if (rv < 0) {
free(op->pdata);
op->pdata = NULL;
return -1;
}
op->plen = op->len;
if (rv < op->len) {
/* we are extending the file! */
/* ftruncate(ts->fs->fd, op->offset + op->len); */
op->plen = rv;
}
return 0;
}
/** Common function to add an operation to a transaction */
static int jtrans_add_common(struct jtrans *ts, const void *buf, size_t count,
off_t offset, enum op_direction direction)
{
struct operation *op, *tmpop;
op = tmpop = NULL;
pthread_mutex_lock(&(ts->lock));
/* Writes are not allowed in read-only mode, they fail early */
if ((ts->flags & J_RDONLY) && direction == D_WRITE)
goto error;
if (count == 0)
goto error;
if ((long long) ts->len_w + count > MAX_TSIZE)
goto error;
op = malloc(sizeof(struct operation));
if (op == NULL)
goto error;
if (direction == D_WRITE) {
op->buf = malloc(count);
if (op->buf == NULL)
goto error;
ts->numops_w++;
} else {
ts->numops_r++;
}
/* add op to the end of the linked list */
op->next = NULL;
if (ts->op == NULL) {
ts->op = op;
op->prev = NULL;
} else {
for (tmpop = ts->op; tmpop->next != NULL; tmpop = tmpop->next)
;
tmpop->next = op;
op->prev = tmpop;
}
pthread_mutex_unlock(&(ts->lock));
op->len = count;
op->offset = offset;
op->plen = 0;
op->pdata = NULL;
op->locked = 0;
op->direction = direction;
if (direction == D_WRITE) {
memcpy(op->buf, buf, count);
if (!(ts->flags & J_NOROLLBACK)) {
/* jtrans_commit() will want to read the current data,
* so we tell the kernel about that */
posix_fadvise(ts->fs->fd, offset, count,
POSIX_FADV_WILLNEED);
}
} else {
/* this casts the const away, which is ugly but let us have a
* common read/write path and avoid useless code repetition
* just to handle it */
op->buf = (void *) buf;
/* if there are no overlapping writes, jtrans_commit() will
* want to read the data from the disk; and if there are we
* will already have submitted a request and one more won't
* hurt */
posix_fadvise(ts->fs->fd, offset, count, POSIX_FADV_WILLNEED);
}
return 0;
error:
pthread_mutex_unlock(&(ts->lock));
if (op && direction == D_WRITE)
free(op->buf);
free(op);
return -1;
}
int jtrans_add_r(struct jtrans *ts, void *buf, size_t count, off_t offset)
{
return jtrans_add_common(ts, buf, count, offset, D_READ);
}
int jtrans_add_w(struct jtrans *ts, const void *buf, size_t count,
off_t offset)
{
return jtrans_add_common(ts, buf, count, offset, D_WRITE);
}
/* Commit a transaction */
ssize_t jtrans_commit(struct jtrans *ts)
{
ssize_t r, retval = -1;
struct operation *op;
struct jlinger *linger;
jop_t *jop = NULL;
size_t written = 0;
pthread_mutex_lock(&(ts->lock));
/* clear the flags */
libjio/libjio/trans.c view on Meta::CPAN
return retval;
}
/* Rollback a transaction */
ssize_t jtrans_rollback(struct jtrans *ts)
{
ssize_t rv;
struct jtrans *newts;
struct operation *op, *curop, *lop;
newts = jtrans_new(ts->fs, 0);
if (newts == NULL)
return -1;
newts->flags = ts->flags;
newts->numops_r = 0;
newts->numops_w = 0;
newts->len_w = 0;
if (ts->op == NULL || ts->flags & J_NOROLLBACK) {
rv = -1;
goto exit;
}
/* find the last operation */
for (op = ts->op; op->next != NULL; op = op->next)
;
/* and traverse the list backwards, skipping read operations */
for ( ; op != NULL; op = op->prev) {
if (op->direction == D_READ)
continue;
/* if we extended the data in the previous transaction, we
* should truncate it back */
/* DANGEROUS: this is one of the main reasons why rollbacking
* is dangerous and should only be done with extreme caution:
* if for some reason, after the previous transacton, we have
* extended the file further, this will cut it back to what it
* was; read the docs for more detail */
if (op->plen < op->len) {
rv = ftruncate(ts->fs->fd, op->offset + op->plen);
if (rv != 0)
goto exit;
}
/* manually add the operation to the new transaction */
curop = malloc(sizeof(struct operation));
if (curop == NULL) {
rv = -1;
goto exit;
}
curop->offset = op->offset;
curop->len = op->plen;
curop->buf = op->pdata;
curop->plen = op->plen;
curop->pdata = op->pdata;
curop->direction = op->direction;
curop->locked = 0;
newts->numops_w++;
newts->len_w += curop->len;
/* add the new transaction to the list */
if (newts->op == NULL) {
newts->op = curop;
curop->prev = NULL;
curop->next = NULL;
} else {
for (lop = newts->op; lop->next != NULL; lop = lop->next)
;
lop->next = curop;
curop->prev = lop;
curop->next = NULL;
}
}
rv = jtrans_commit(newts);
exit:
/* free the transaction */
for (curop = newts->op; curop != NULL; curop = curop->next) {
curop->buf = NULL;
curop->pdata = NULL;
}
jtrans_free(newts);
return rv;
}
/*
* Basic operations
*/
/* Open a file */
struct jfs *jopen(const char *name, int flags, int mode, unsigned int jflags)
{
int jfd, rv;
unsigned int t;
char jdir[PATH_MAX], jlockfile[PATH_MAX];
struct stat sinfo;
pthread_mutexattr_t attr;
struct jfs *fs;
fs = malloc(sizeof(struct jfs));
if (fs == NULL)
return NULL;
fs->fd = -1;
fs->jfd = -1;
fs->jdir = NULL;
fs->jdirfd = -1;
fs->jmap = MAP_FAILED;
fs->as_cfg = NULL;
/* we provide either read-only or read-write access, because when we
* commit a transaction we read the current contents before applying,
* and write access is needed for locking with fcntl; the test is done
( run in 1.743 second using v1.01-cache-2.11-cpan-524268b4103 )