xref: /aosp_15_r20/external/erofs-utils/lib/diskbuf.c (revision 33b1fccf6a0fada2c2875d400ed01119b7676ee5)
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