EXPORTED int mappedfile_writelock(struct mappedfile *mf) { int r; struct stat sbuf; const char *lockfailaction; int changed = 0; assert(mf->lock_status == MF_UNLOCKED); assert(mf->fd != -1); assert(mf->is_rw); assert(!mf->dirty); r = lock_reopen_ex(mf->fd, mf->fname, &sbuf, &lockfailaction, &changed); if (r < 0) { syslog(LOG_ERR, "IOERROR: %s %s: %m", lockfailaction, mf->fname); return r; } mf->lock_status = MF_WRITELOCKED; gettimeofday(&mf->starttime, 0); if (changed) buf_free(&mf->map_buf); _ensure_mapped(mf, sbuf.st_size, /*update*/0); return 0; }
EXPORTED int mappedfile_writelock(struct mappedfile *mf) { int r; struct stat sbuf; const char *lockfailaction; assert(mf->lock_status == MF_UNLOCKED); assert(mf->fd != -1); assert(mf->is_rw); assert(!mf->dirty); r = lock_reopen(mf->fd, mf->fname, &sbuf, &lockfailaction); if (r < 0) { syslog(LOG_ERR, "IOERROR: %s %s: %m", lockfailaction, mf->fname); return r; } mf->lock_status = MF_WRITELOCKED; /* XXX - can we guarantee the fd isn't reused? */ if (mf->map_ino != sbuf.st_ino) { buf_free(&mf->map_buf); } _ensure_mapped(mf, sbuf.st_size, /*update*/0); return 0; }
/* NOTE - we don't provide any guarantees that the file isn't open multiple * times. So don't do that. It will mess with your locking no end */ EXPORTED int mappedfile_open(struct mappedfile **mfp, const char *fname, int flags) { struct mappedfile *mf; struct stat sbuf; int openmode = (flags & MAPPEDFILE_RW) ? O_RDWR : O_RDONLY; int create = (flags & MAPPEDFILE_CREATE) ? 1 : 0; int r; assert(fname); assert(!*mfp); mf = xzmalloc(sizeof(struct mappedfile)); mf->fname = xstrdup(fname); mf->is_rw = (flags & MAPPEDFILE_RW) ? 1 : 0; mf->fd = open(mf->fname, openmode, 0644); if (mf->fd < 0 && errno == ENOENT) { if (!create || !mf->is_rw) { r = -errno; goto err; } r = cyrus_mkdir(mf->fname, 0755); if (r < 0) { syslog(LOG_ERR, "IOERROR: cyrus_mkdir %s: %m", mf->fname); goto err; } mf->fd = open(mf->fname, O_RDWR | O_CREAT, 0644); } if (mf->fd == -1) { syslog(LOG_ERR, "IOERROR: open %s: %m", mf->fname); r = -errno; goto err; } /* it's zero, but set it anyway */ mf->lock_status = MF_UNLOCKED; mf->dirty = 0; r = fstat(mf->fd, &sbuf); if (r < 0) { syslog(LOG_ERR, "IOERROR: fstat %s: %m", mf->fname); goto err; } _ensure_mapped(mf, sbuf.st_size, /*update*/0); *mfp = mf; return 0; err: mappedfile_close(&mf); return r; }
EXPORTED int mappedfile_readlock(struct mappedfile *mf) { struct stat sbuf, sbuffile; int newfd = -1; assert(mf->lock_status == MF_UNLOCKED); assert(mf->fd != -1); assert(!mf->dirty); for (;;) { if (lock_shared(mf->fd, mf->fname) < 0) { syslog(LOG_ERR, "IOERROR: lock_shared %s: %m", mf->fname); return -EIO; } if (fstat(mf->fd, &sbuf) == -1) { syslog(LOG_ERR, "IOERROR: fstat %s: %m", mf->fname); lock_unlock(mf->fd, mf->fname); return -EIO; } if (stat(mf->fname, &sbuffile) == -1) { syslog(LOG_ERR, "IOERROR: stat %s: %m", mf->fname); lock_unlock(mf->fd, mf->fname); return -EIO; } if (sbuf.st_ino == sbuffile.st_ino) break; newfd = open(mf->fname, O_RDWR, 0644); if (newfd == -1) { syslog(LOG_ERR, "IOERROR: open %s: %m", mf->fname); lock_unlock(mf->fd, mf->fname); return -EIO; } dup2(newfd, mf->fd); close(newfd); } mf->lock_status = MF_READLOCKED; /* XXX - can we guarantee the fd isn't reused? */ if (mf->map_ino != sbuf.st_ino) { buf_free(&mf->map_buf); } _ensure_mapped(mf, sbuf.st_size, /*update*/0); return 0; }
EXPORTED ssize_t mappedfile_pwritev(struct mappedfile *mf, const struct iovec *iov, int nio, off_t offset) { ssize_t written; off_t pos; assert(mf->is_rw); assert(mf->fd != -1); assert(iov); if (!nio) return 0; /* nothing to write! */ /* XXX - memcmp and don't both writing if it matches? */ mf->dirty++; /* locate the file handle */ pos = lseek(mf->fd, offset, SEEK_SET); if (pos < 0) { syslog(LOG_ERR, "IOERROR: %s seek to %llX: %m", mf->fname, (long long unsigned int)offset); return -1; } /* write the buffer */ written = retry_writev(mf->fd, iov, nio); if (written < 0) { size_t len = 0; int i; for (i = 0; i < nio; i++) { len += iov[i].iov_len; } syslog(LOG_ERR, "IOERROR: %s write %llu bytes at %llX: %m", mf->fname, (long long unsigned int)len, (long long unsigned int)offset); return -1; } _ensure_mapped(mf, pos+written, /*update*/1); return written; }
EXPORTED int mappedfile_truncate(struct mappedfile *mf, off_t offset) { int r; assert(mf->is_rw); assert(mf->fd != -1); mf->dirty++; r = ftruncate(mf->fd, offset); if (r < 0) { syslog(LOG_ERR, "IOERROR: ftruncate %s: %m", mf->fname); return r; } _ensure_mapped(mf, offset, /*update*/0); mf->was_resized = 1; /* force the fsync */ return 0; }
EXPORTED ssize_t mappedfile_pwrite(struct mappedfile *mf, const void *base, size_t len, off_t offset) { ssize_t written; off_t pos; assert(mf->is_rw); assert(mf->fd != -1); assert(base); if (!len) return 0; /* nothing to write! */ /* XXX - memcmp and don't both writing if it matches? */ mf->dirty++; /* locate the file handle */ pos = lseek(mf->fd, offset, SEEK_SET); if (pos < 0) { syslog(LOG_ERR, "IOERROR: %s seek to %llX: %m", mf->fname, (long long unsigned int)offset); return -1; } /* write the buffer */ written = retry_write(mf->fd, base, len); if (written < 0) { syslog(LOG_ERR, "IOERROR: %s write %llu bytes at %llX: %m", mf->fname, (long long unsigned int)len, (long long unsigned int)offset); return -1; } _ensure_mapped(mf, pos+written, /*update*/1); return written; }