• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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