1 // SPDX-License-Identifier: GPL-2.0+ OR Apache-2.0
2 #include "erofs/diskbuf.h"
3 #include "erofs/internal.h"
4 #include "erofs/print.h"
5 #include <stdio.h>
6 #include <errno.h>
7 #include <sys/stat.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10
11 /* A simple approach to avoid creating too many temporary files */
12 static struct erofs_diskbufstrm {
13 erofs_atomic_t count;
14 u64 tailoffset, devpos;
15 int fd;
16 unsigned int alignsize;
17 bool locked;
18 } *dbufstrm;
19
erofs_diskbuf_getfd(struct erofs_diskbuf * db,u64 * fpos)20 int erofs_diskbuf_getfd(struct erofs_diskbuf *db, u64 *fpos)
21 {
22 const struct erofs_diskbufstrm *strm = db->sp;
23 u64 offset;
24
25 if (!strm)
26 return -1;
27 offset = db->offset + strm->devpos;
28 if (fpos)
29 *fpos = offset;
30 return strm->fd;
31 }
32
erofs_diskbuf_reserve(struct erofs_diskbuf * db,int sid,u64 * off)33 int erofs_diskbuf_reserve(struct erofs_diskbuf *db, int sid, u64 *off)
34 {
35 struct erofs_diskbufstrm *strm = dbufstrm + sid;
36
37 if (strm->tailoffset & (strm->alignsize - 1)) {
38 strm->tailoffset = round_up(strm->tailoffset, strm->alignsize);
39 if (lseek(strm->fd, strm->tailoffset + strm->devpos,
40 SEEK_SET) != strm->tailoffset + strm->devpos)
41 return -EIO;
42 }
43 db->offset = strm->tailoffset;
44 if (off)
45 *off = db->offset + strm->devpos;
46 db->sp = strm;
47 (void)erofs_atomic_inc_return(&strm->count);
48 strm->locked = true; /* TODO: need a real lock for MT */
49 return strm->fd;
50 }
51
erofs_diskbuf_commit(struct erofs_diskbuf * db,u64 len)52 void erofs_diskbuf_commit(struct erofs_diskbuf *db, u64 len)
53 {
54 struct erofs_diskbufstrm *strm = db->sp;
55
56 DBG_BUGON(!strm);
57 DBG_BUGON(!strm->locked);
58 DBG_BUGON(strm->tailoffset != db->offset);
59 strm->tailoffset += len;
60 }
61
erofs_diskbuf_close(struct erofs_diskbuf * db)62 void erofs_diskbuf_close(struct erofs_diskbuf *db)
63 {
64 struct erofs_diskbufstrm *strm = db->sp;
65
66 DBG_BUGON(!strm);
67 DBG_BUGON(erofs_atomic_read(&strm->count) <= 1);
68 (void)erofs_atomic_dec_return(&strm->count);
69 db->sp = NULL;
70 }
71
erofs_tmpfile(void)72 int erofs_tmpfile(void)
73 {
74 #define TRAILER "tmp.XXXXXXXXXX"
75 char buf[PATH_MAX];
76 int fd;
77 umode_t u;
78
79 (void)snprintf(buf, sizeof(buf), "%s/" TRAILER,
80 getenv("TMPDIR") ?: "/tmp");
81
82 fd = mkstemp(buf);
83 if (fd < 0)
84 return -errno;
85
86 unlink(buf);
87 u = umask(0);
88 (void)umask(u);
89 (void)fchmod(fd, 0666 & ~u);
90 return fd;
91 }
92
erofs_diskbuf_init(unsigned int nstrms)93 int erofs_diskbuf_init(unsigned int nstrms)
94 {
95 struct erofs_diskbufstrm *strm;
96
97 strm = calloc(nstrms + 1, sizeof(*strm));
98 if (!strm)
99 return -ENOMEM;
100 strm[nstrms].fd = -1;
101 dbufstrm = strm;
102
103 for (; strm < dbufstrm + nstrms; ++strm) {
104 struct stat st;
105
106 /* try to use the devfd for regfiles on stream 0 */
107 if (strm == dbufstrm && !g_sbi.bdev.ops) {
108 strm->devpos = 1ULL << 40;
109 if (!ftruncate(g_sbi.bdev.fd, strm->devpos << 1)) {
110 strm->fd = dup(g_sbi.bdev.fd);
111 if (lseek(strm->fd, strm->devpos,
112 SEEK_SET) != strm->devpos)
113 return -EIO;
114 goto setupone;
115 }
116 }
117 strm->devpos = 0;
118 strm->fd = erofs_tmpfile();
119 if (strm->fd < 0)
120 return -ENOSPC;
121 setupone:
122 strm->tailoffset = 0;
123 erofs_atomic_set(&strm->count, 1);
124 if (fstat(strm->fd, &st))
125 return -errno;
126 strm->alignsize = max_t(u32, st.st_blksize, getpagesize());
127 }
128 return 0;
129 }
130
erofs_diskbuf_exit(void)131 void erofs_diskbuf_exit(void)
132 {
133 struct erofs_diskbufstrm *strm;
134
135 if (!dbufstrm)
136 return;
137
138 for (strm = dbufstrm; strm->fd >= 0; ++strm) {
139 DBG_BUGON(erofs_atomic_read(&strm->count) != 1);
140
141 close(strm->fd);
142 strm->fd = -1;
143 }
144 }
145