// SPDX-License-Identifier: GPL-2.0+ OR Apache-2.0 #include "erofs/diskbuf.h" #include "erofs/internal.h" #include "erofs/print.h" #include #include #include #include #include /* A simple approach to avoid creating too many temporary files */ static struct erofs_diskbufstrm { erofs_atomic_t count; u64 tailoffset, devpos; int fd; unsigned int alignsize; bool locked; } *dbufstrm; int erofs_diskbuf_getfd(struct erofs_diskbuf *db, u64 *fpos) { const struct erofs_diskbufstrm *strm = db->sp; u64 offset; if (!strm) return -1; offset = db->offset + strm->devpos; if (fpos) *fpos = offset; return strm->fd; } int erofs_diskbuf_reserve(struct erofs_diskbuf *db, int sid, u64 *off) { struct erofs_diskbufstrm *strm = dbufstrm + sid; if (strm->tailoffset & (strm->alignsize - 1)) { strm->tailoffset = round_up(strm->tailoffset, strm->alignsize); if (lseek(strm->fd, strm->tailoffset + strm->devpos, SEEK_SET) != strm->tailoffset + strm->devpos) return -EIO; } db->offset = strm->tailoffset; if (off) *off = db->offset + strm->devpos; db->sp = strm; (void)erofs_atomic_inc_return(&strm->count); strm->locked = true; /* TODO: need a real lock for MT */ return strm->fd; } void erofs_diskbuf_commit(struct erofs_diskbuf *db, u64 len) { struct erofs_diskbufstrm *strm = db->sp; DBG_BUGON(!strm); DBG_BUGON(!strm->locked); DBG_BUGON(strm->tailoffset != db->offset); strm->tailoffset += len; } void erofs_diskbuf_close(struct erofs_diskbuf *db) { struct erofs_diskbufstrm *strm = db->sp; DBG_BUGON(!strm); DBG_BUGON(erofs_atomic_read(&strm->count) <= 1); (void)erofs_atomic_dec_return(&strm->count); db->sp = NULL; } int erofs_tmpfile(void) { #define TRAILER "tmp.XXXXXXXXXX" char buf[PATH_MAX]; int fd; umode_t u; (void)snprintf(buf, sizeof(buf), "%s/" TRAILER, getenv("TMPDIR") ?: "/tmp"); fd = mkstemp(buf); if (fd < 0) return -errno; unlink(buf); u = umask(0); (void)umask(u); (void)fchmod(fd, 0666 & ~u); return fd; } int erofs_diskbuf_init(unsigned int nstrms) { struct erofs_diskbufstrm *strm; strm = calloc(nstrms + 1, sizeof(*strm)); if (!strm) return -ENOMEM; strm[nstrms].fd = -1; dbufstrm = strm; for (; strm < dbufstrm + nstrms; ++strm) { struct stat st; /* try to use the devfd for regfiles on stream 0 */ if (strm == dbufstrm && !g_sbi.bdev.ops) { strm->devpos = 1ULL << 40; if (!ftruncate(g_sbi.bdev.fd, strm->devpos << 1)) { strm->fd = dup(g_sbi.bdev.fd); if (lseek(strm->fd, strm->devpos, SEEK_SET) != strm->devpos) return -EIO; goto setupone; } } strm->devpos = 0; strm->fd = erofs_tmpfile(); if (strm->fd < 0) return -ENOSPC; setupone: strm->tailoffset = 0; erofs_atomic_set(&strm->count, 1); if (fstat(strm->fd, &st)) return -errno; strm->alignsize = max_t(u32, st.st_blksize, getpagesize()); } return 0; } void erofs_diskbuf_exit(void) { struct erofs_diskbufstrm *strm; if (!dbufstrm) return; for (strm = dbufstrm; strm->fd >= 0; ++strm) { DBG_BUGON(erofs_atomic_read(&strm->count) != 1); close(strm->fd); strm->fd = -1; } }