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 u64 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 (lseek(strm->fd, offset, SEEK_SET) != offset)
29 return -E2BIG;
30 if (fpos)
31 *fpos = offset;
32 return strm->fd;
33 }
34
erofs_diskbuf_reserve(struct erofs_diskbuf * db,int sid,u64 * off)35 int erofs_diskbuf_reserve(struct erofs_diskbuf *db, int sid, u64 *off)
36 {
37 struct erofs_diskbufstrm *strm = dbufstrm + sid;
38
39 if (strm->tailoffset & (strm->alignsize - 1)) {
40 strm->tailoffset = round_up(strm->tailoffset, strm->alignsize);
41 if (lseek(strm->fd, strm->tailoffset + strm->devpos,
42 SEEK_SET) != strm->tailoffset + strm->devpos)
43 return -EIO;
44 }
45 db->offset = strm->tailoffset;
46 if (off)
47 *off = db->offset + strm->devpos;
48 db->sp = strm;
49 ++strm->count;
50 strm->locked = true; /* TODO: need a real lock for MT */
51 return strm->fd;
52 }
53
erofs_diskbuf_commit(struct erofs_diskbuf * db,u64 len)54 void erofs_diskbuf_commit(struct erofs_diskbuf *db, u64 len)
55 {
56 struct erofs_diskbufstrm *strm = db->sp;
57
58 DBG_BUGON(!strm);
59 DBG_BUGON(!strm->locked);
60 DBG_BUGON(strm->tailoffset != db->offset);
61 strm->tailoffset += len;
62 }
63
erofs_diskbuf_close(struct erofs_diskbuf * db)64 void erofs_diskbuf_close(struct erofs_diskbuf *db)
65 {
66 struct erofs_diskbufstrm *strm = db->sp;
67
68 DBG_BUGON(!strm);
69 DBG_BUGON(strm->count <= 1);
70 --strm->count;
71 db->sp = NULL;
72 }
73
erofs_tmpfile(void)74 int erofs_tmpfile(void)
75 {
76 #define TRAILER "tmp.XXXXXXXXXX"
77 char buf[PATH_MAX];
78 int fd;
79 umode_t u;
80
81 (void)snprintf(buf, sizeof(buf), "%s/" TRAILER,
82 getenv("TMPDIR") ?: "/tmp");
83
84 fd = mkstemp(buf);
85 if (fd < 0)
86 return -errno;
87
88 unlink(buf);
89 u = umask(0);
90 (void)umask(u);
91 (void)fchmod(fd, 0666 & ~u);
92 return fd;
93 }
94
erofs_diskbuf_init(unsigned int nstrms)95 int erofs_diskbuf_init(unsigned int nstrms)
96 {
97 struct erofs_diskbufstrm *strm;
98
99 strm = calloc(nstrms + 1, sizeof(*strm));
100 if (!strm)
101 return -ENOMEM;
102 strm[nstrms].fd = -1;
103 dbufstrm = strm;
104
105 for (; strm < dbufstrm + nstrms; ++strm) {
106 struct stat st;
107
108 /* try to use the devfd for regfiles on stream 0 */
109 if (strm == dbufstrm && sbi.devsz == INT64_MAX) {
110 strm->devpos = 1ULL << 40;
111 if (!ftruncate(sbi.devfd, strm->devpos << 1)) {
112 strm->fd = dup(sbi.devfd);
113 if (lseek(strm->fd, strm->devpos,
114 SEEK_SET) != strm->devpos)
115 return -EIO;
116 goto setupone;
117 }
118 }
119 strm->devpos = 0;
120 strm->fd = erofs_tmpfile();
121 if (strm->fd < 0)
122 return -ENOSPC;
123 setupone:
124 strm->tailoffset = 0;
125 strm->count = 1;
126 if (fstat(strm->fd, &st))
127 return -errno;
128 strm->alignsize = max_t(u32, st.st_blksize, getpagesize());
129 }
130 return 0;
131 }
132
erofs_diskbuf_exit(void)133 void erofs_diskbuf_exit(void)
134 {
135 struct erofs_diskbufstrm *strm;
136
137 if (!dbufstrm)
138 return;
139
140 for (strm = dbufstrm; strm->fd >= 0; ++strm) {
141 DBG_BUGON(strm->count != 1);
142
143 close(strm->fd);
144 strm->fd = -1;
145 }
146 }
147