[PATCH 2/3] erofs-utils: lib: get rid of tmpfile()

Gao Xiang hsiangkao at linux.alibaba.com
Fri Feb 14 17:24:06 AEDT 2025


Currently, `tmpfile()` is problematic:

 - It uses `FILE *` instead of `fd`;
 - It may not leverage `$TMPDIR`, see `__gen_tempfd()`:
   https://sourceware.org/git?p=glibc.git;a=blob;f=stdio-common/tmpfile.c;hb=glibc-2.41

Switch to `erofs_tmpfile()` throughout the codebase.

Signed-off-by: Gao Xiang <hsiangkao at linux.alibaba.com>
---
 configure.ac              |  1 -
 include/erofs/fragments.h |  2 +-
 include/erofs/io.h        |  2 +
 lib/blobchunk.c           | 44 +++++++++---------
 lib/fragments.c           | 94 +++++++++++++++++++--------------------
 lib/io.c                  | 22 +++++++++
 lib/liberofs_private.h    |  2 +
 lib/xattr.c               | 23 ++++++----
 8 files changed, 110 insertions(+), 80 deletions(-)

diff --git a/configure.ac b/configure.ac
index c9b4acf..6e1e7a1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -281,7 +281,6 @@ AC_CHECK_FUNCS(m4_flatten([
 	strrchr
 	strtoull
 	sysconf
-	tmpfile64
 	utimensat]))
 
 # Detect maximum block size if necessary
diff --git a/include/erofs/fragments.h b/include/erofs/fragments.h
index 14a1b4a..ccfdd9b 100644
--- a/include/erofs/fragments.h
+++ b/include/erofs/fragments.h
@@ -22,7 +22,7 @@ int z_erofs_pack_file_from_fd(struct erofs_inode *inode, int fd, u32 tofcrc);
 int z_erofs_pack_fragments(struct erofs_inode *inode, void *data,
 			   unsigned int len, u32 tofcrc);
 int erofs_flush_packed_inode(struct erofs_sb_info *sbi);
-FILE *erofs_packedfile(struct erofs_sb_info *sbi);
+int erofs_packedfile(struct erofs_sb_info *sbi);
 
 int erofs_packedfile_init(struct erofs_sb_info *sbi, bool fragments_mkfs);
 void erofs_packedfile_exit(struct erofs_sb_info *sbi);
diff --git a/include/erofs/io.h b/include/erofs/io.h
index d9b33d2..3179ea1 100644
--- a/include/erofs/io.h
+++ b/include/erofs/io.h
@@ -49,6 +49,8 @@ struct erofs_vfile {
 	};
 };
 
+ssize_t __erofs_io_write(int fd, const void *buf, size_t len);
+
 int erofs_io_fstat(struct erofs_vfile *vf, struct stat *buf);
 ssize_t erofs_io_pwrite(struct erofs_vfile *vf, const void *buf, u64 pos, size_t len);
 int erofs_io_fsync(struct erofs_vfile *vf);
diff --git a/lib/blobchunk.c b/lib/blobchunk.c
index 8e2360f..5b8a6db 100644
--- a/lib/blobchunk.c
+++ b/lib/blobchunk.c
@@ -9,6 +9,7 @@
 #include "erofs/blobchunk.h"
 #include "erofs/block_list.h"
 #include "erofs/cache.h"
+#include "liberofs_private.h"
 #include "sha256.h"
 #include <unistd.h>
 
@@ -27,7 +28,7 @@ struct erofs_blobchunk {
 };
 
 static struct hashmap blob_hashmap;
-static FILE *blobfile;
+static int blobfile = -1;
 static erofs_blk_t remapped_base;
 static erofs_off_t datablob_size;
 static bool multidev;
@@ -86,7 +87,7 @@ static struct erofs_blobchunk *erofs_blob_getchunk(struct erofs_sb_info *sbi,
 
 	chunk->chunksize = chunksize;
 	memcpy(chunk->sha256, sha256, sizeof(sha256));
-	blkpos = ftell(blobfile);
+	blkpos = lseek(blobfile, 0, SEEK_CUR);
 	DBG_BUGON(erofs_blkoff(sbi, blkpos));
 
 	if (sbi->extra_devices)
@@ -97,18 +98,22 @@ static struct erofs_blobchunk *erofs_blob_getchunk(struct erofs_sb_info *sbi,
 
 	erofs_dbg("Writing chunk (%llu bytes) to %u", chunksize | 0ULL,
 		  chunk->blkaddr);
-	ret = fwrite(buf, chunksize, 1, blobfile);
-	if (ret == 1) {
+	ret = __erofs_io_write(blobfile, buf, chunksize);
+	if (ret == chunksize) {
 		padding = erofs_blkoff(sbi, chunksize);
 		if (padding) {
 			padding = erofs_blksiz(sbi) - padding;
-			ret = fwrite(zeroed, padding, 1, blobfile);
+			ret = __erofs_io_write(blobfile, zeroed, padding);
+			if (ret > 0 && ret != padding)
+				ret = -EIO;
 		}
+	} else if (ret > 0) {
+		ret = -EIO;
 	}
 
-	if (ret < 1) {
+	if (ret < 0) {
 		free(chunk);
-		return ERR_PTR(-ENOSPC);
+		return ERR_PTR(-errno);
 	}
 
 	hashmap_entry_init(&chunk->ent, hash);
@@ -488,9 +493,8 @@ int erofs_mkfs_dump_blobs(struct erofs_sb_info *sbi)
 	ssize_t length, ret;
 	u64 pos_in, pos_out;
 
-	if (blobfile) {
-		fflush(blobfile);
-		length = ftell(blobfile);
+	if (blobfile >= 0) {
+		length = lseek(blobfile, 0, SEEK_CUR);
 		if (length < 0)
 			return -errno;
 
@@ -534,11 +538,11 @@ int erofs_mkfs_dump_blobs(struct erofs_sb_info *sbi)
 	pos_out = erofs_btell(bh, false);
 	remapped_base = erofs_blknr(sbi, pos_out);
 	pos_out += sbi->bdev.offset;
-	if (blobfile) {
+	if (blobfile >= 0) {
 		pos_in = 0;
 		do {
 			length = min_t(erofs_off_t, datablob_size,  SSIZE_MAX);
-			ret = erofs_copy_file_range(fileno(blobfile), &pos_in,
+			ret = erofs_copy_file_range(blobfile, &pos_in,
 					sbi->bdev.fd, &pos_out, length);
 		} while (ret > 0 && (datablob_size -= ret));
 
@@ -565,8 +569,8 @@ void erofs_blob_exit(void)
 	struct hashmap_entry *e;
 	struct erofs_blobchunk *bc, *n;
 
-	if (blobfile)
-		fclose(blobfile);
+	if (blobfile >= 0)
+		close(blobfile);
 
 	/* Disable hashmap shrink, effectively disabling rehash.
 	 * This way we can iterate over entire hashmap efficiently
@@ -620,18 +624,14 @@ static int erofs_insert_zerochunk(erofs_off_t chunksize)
 int erofs_blob_init(const char *blobfile_path, erofs_off_t chunksize)
 {
 	if (!blobfile_path) {
-#ifdef HAVE_TMPFILE64
-		blobfile = tmpfile64();
-#else
-		blobfile = tmpfile();
-#endif
+		blobfile = erofs_tmpfile();
 		multidev = false;
 	} else {
-		blobfile = fopen(blobfile_path, "wb");
+		blobfile = open(blobfile_path, O_WRONLY | O_BINARY);
 		multidev = true;
 	}
-	if (!blobfile)
-		return -EACCES;
+	if (blobfile < 0)
+		return -errno;
 
 	hashmap_init(&blob_hashmap, erofs_blob_hashmap_cmp, 0);
 	return erofs_insert_zerochunk(chunksize);
diff --git a/lib/fragments.c b/lib/fragments.c
index 9633a2b..3e97f14 100644
--- a/lib/fragments.c
+++ b/lib/fragments.c
@@ -3,9 +3,6 @@
  * Copyright (C), 2022, Coolpad Group Limited.
  * Created by Yue Hu <huyue2 at coolpad.com>
  */
-#ifndef _LARGEFILE_SOURCE
-#define _LARGEFILE_SOURCE
-#endif
 #ifndef _LARGEFILE64_SOURCE
 #define _LARGEFILE64_SOURCE
 #endif
@@ -25,6 +22,7 @@
 #include "erofs/internal.h"
 #include "erofs/fragments.h"
 #include "erofs/bitops.h"
+#include "liberofs_private.h"
 
 struct erofs_fragment_dedupe_item {
 	struct list_head	list;
@@ -41,7 +39,7 @@ struct erofs_fragment_dedupe_item {
 
 struct erofs_packed_inode {
 	struct list_head *hash;
-	FILE *file;
+	int fd;
 	unsigned long *uptodate;
 #if EROFS_MT_ENABLED
 	pthread_mutex_t mutex;
@@ -108,8 +106,7 @@ static int z_erofs_fragments_dedupe_find(struct erofs_inode *inode, int fd,
 
 				sz = min_t(u64, pos, sizeof(buf[0]));
 				sz = min_t(u64, sz, inode->i_size - i);
-				if (pread(fileno(epi->file), buf[0], sz,
-					  pos - sz) != sz)
+				if (pread(epi->fd, buf[0], sz, pos - sz) != sz)
 					break;
 				if (pread(fd, buf[1], sz,
 					  inode->i_size - i - sz) != sz)
@@ -208,14 +205,10 @@ void z_erofs_fragments_commit(struct erofs_inode *inode)
 int z_erofs_pack_file_from_fd(struct erofs_inode *inode, int fd, u32 tofcrc)
 {
 	struct erofs_packed_inode *epi = inode->sbi->packedinode;
-#ifdef HAVE_FTELLO64
-	off64_t offset = ftello64(epi->file);
-#else
-	off_t offset = ftello(epi->file);
-#endif
+	s64 offset, rc;
 	char *memblock;
-	int rc;
 
+	offset = lseek(epi->fd, 0, SEEK_CUR);
 	if (offset < 0)
 		return -errno;
 
@@ -233,15 +226,22 @@ int z_erofs_pack_file_from_fd(struct erofs_inode *inode, int fd, u32 tofcrc)
 						sizeof(buf));
 
 			rc = read(fd, buf, sz);
+			if (rc != sz) {
+				if (rc <= 0) {
+					if (!rc)
+						rc = -EIO;
+					else
+						rc = -errno;
+					goto out;
+				}
+				sz = rc;
+			}
+			rc = __erofs_io_write(epi->fd, buf, sz);
 			if (rc != sz) {
 				if (rc < 0)
 					rc = -errno;
 				else
-					rc = -EAGAIN;
-				goto out;
-			}
-			if (fwrite(buf, sz, 1, epi->file) != 1) {
-				rc = -EIO;
+					rc = -EIO;
 				goto out;
 			}
 			remaining -= sz;
@@ -251,9 +251,15 @@ int z_erofs_pack_file_from_fd(struct erofs_inode *inode, int fd, u32 tofcrc)
 			rc = -errno;
 			goto out;
 		}
-	} else if (fwrite(memblock, inode->fragment_size, 1, epi->file) != 1) {
-		rc = -EIO;
-		goto out;
+	} else {
+		rc = __erofs_io_write(epi->fd, memblock, inode->fragment_size);
+		if (rc != inode->fragment_size) {
+			if (rc < 0)
+				rc = -errno;
+			else
+				rc = -EIO;
+			goto out;
+		}
 	}
 
 	erofs_dbg("Recording %llu fragment data at %llu",
@@ -279,11 +285,7 @@ int z_erofs_pack_fragments(struct erofs_inode *inode, void *data,
 			   unsigned int len, u32 tofcrc)
 {
 	struct erofs_packed_inode *epi = inode->sbi->packedinode;
-#ifdef HAVE_FTELLO64
-	off64_t offset = ftello64(epi->file);
-#else
-	off_t offset = ftello(epi->file);
-#endif
+	s64 offset = lseek(epi->fd, 0, SEEK_CUR);
 	int ret;
 
 	if (offset < 0)
@@ -292,8 +294,12 @@ int z_erofs_pack_fragments(struct erofs_inode *inode, void *data,
 	inode->fragmentoff = (erofs_off_t)offset;
 	inode->fragment_size = len;
 
-	if (fwrite(data, len, 1, epi->file) != 1)
+	ret = write(epi->fd, data, len);
+	if (ret != len) {
+		if (ret < 0)
+			return -errno;
 		return -EIO;
+	}
 
 	erofs_dbg("Recording %llu fragment data at %llu",
 		  inode->fragment_size | 0ULL, inode->fragmentoff | 0ULL);
@@ -313,19 +319,18 @@ int erofs_flush_packed_inode(struct erofs_sb_info *sbi)
 	if (!epi || !erofs_sb_has_fragments(sbi))
 		return -EINVAL;
 
-	fflush(epi->file);
-	if (!ftello(epi->file))
+	if (lseek(epi->fd, 0, SEEK_CUR) <= 0)
 		return 0;
-	inode = erofs_mkfs_build_special_from_fd(sbi, fileno(epi->file),
+	inode = erofs_mkfs_build_special_from_fd(sbi, epi->fd,
 						 EROFS_PACKED_INODE);
 	sbi->packed_nid = erofs_lookupnid(inode);
 	erofs_iput(inode);
 	return 0;
 }
 
-FILE *erofs_packedfile(struct erofs_sb_info *sbi)
+int erofs_packedfile(struct erofs_sb_info *sbi)
 {
-	return sbi->packedinode->file;
+	return sbi->packedinode->fd;
 }
 
 void erofs_packedfile_exit(struct erofs_sb_info *sbi)
@@ -347,8 +352,8 @@ void erofs_packedfile_exit(struct erofs_sb_info *sbi)
 		free(epi->hash);
 	}
 
-	if (epi->file)
-		fclose(epi->file);
+	if (epi->fd >= 0)
+		close(epi->fd);
 	free(epi);
 	sbi->packedinode = NULL;
 }
@@ -376,14 +381,9 @@ int erofs_packedfile_init(struct erofs_sb_info *sbi, bool fragments_mkfs)
 			init_list_head(&epi->hash[i]);
 	}
 
-	epi->file =
-#ifdef HAVE_TMPFILE64
-		tmpfile64();
-#else
-		tmpfile();
-#endif
-	if (!epi->file) {
-		err = -errno;
+	epi->fd = erofs_tmpfile();
+	if (epi->fd < 0) {
+		err = epi->fd;
 		goto err_out;
 	}
 
@@ -392,6 +392,7 @@ int erofs_packedfile_init(struct erofs_sb_info *sbi, bool fragments_mkfs)
 			.sbi = sbi,
 			.nid = sbi->packed_nid,
 		};
+		s64 offset;
 
 		err = erofs_read_inode_from_disk(&ei);
 		if (err) {
@@ -400,8 +401,8 @@ int erofs_packedfile_init(struct erofs_sb_info *sbi, bool fragments_mkfs)
 			goto err_out;
 		}
 
-		err = fseek(epi->file, ei.i_size, SEEK_SET);
-		if (err) {
+		offset = lseek(epi->fd, ei.i_size, SEEK_SET);
+		if (offset < 0) {
 			err = -errno;
 			goto err_out;
 		}
@@ -472,13 +473,12 @@ static void *erofs_packedfile_preload(struct erofs_inode *pi,
 	if (err)
 		goto err_out;
 
-	fflush(epi->file);
-	err = pwrite(fileno(epi->file), buffer, map->m_llen, map->m_la);
+	err = pwrite(epi->fd, buffer, map->m_llen, map->m_la);
 	if (err < 0) {
 		err = -errno;
 		if (err == -ENOSPC) {
 			memset(epi->uptodate, 0, epi->uptodate_size);
-			(void)!ftruncate(fileno(epi->file), 0);
+			(void)!ftruncate(epi->fd, 0);
 		}
 		goto err_out;
 	}
@@ -557,7 +557,7 @@ int erofs_packedfile_read(struct erofs_sb_info *sbi,
 			if (!uptodate)
 				continue;
 
-			err = pread(fileno(epi->file), buf, len, pos);
+			err = pread(epi->fd, buf, len, pos);
 			if (err < 0)
 				break;
 			if (err == len) {
diff --git a/lib/io.c b/lib/io.c
index dacf8dc..b6eb22a 100644
--- a/lib/io.c
+++ b/lib/io.c
@@ -26,6 +26,28 @@
 #define EROFS_MODNAME	"erofs_io"
 #include "erofs/print.h"
 
+ssize_t __erofs_io_write(int fd, const void *buf, size_t len)
+{
+	ssize_t ret, written = 0;
+
+	do {
+		ret = write(fd, buf, len);
+		if (ret <= 0) {
+			if (!ret)
+				break;
+			if (errno != EINTR) {
+				erofs_err("failed to write: %s", strerror(errno));
+				return -errno;
+			}
+			ret = 0;
+		}
+		buf += ret;
+		written += ret;
+	} while (written < len);
+
+	return written;
+}
+
 int erofs_io_fstat(struct erofs_vfile *vf, struct stat *buf)
 {
 	if (__erofs_unlikely(cfg.c_dry_run)) {
diff --git a/lib/liberofs_private.h b/lib/liberofs_private.h
index 0eeca3c..ebd9e70 100644
--- a/lib/liberofs_private.h
+++ b/lib/liberofs_private.h
@@ -23,3 +23,5 @@ static inline void *memrchr(const void *s, int c, size_t n)
 	return NULL;
 }
 #endif
+
+int erofs_tmpfile(void);
diff --git a/lib/xattr.c b/lib/xattr.c
index 063b01a..cb44786 100644
--- a/lib/xattr.c
+++ b/lib/xattr.c
@@ -811,21 +811,22 @@ static int comp_shared_xattr_item(const void *a, const void *b)
 
 int erofs_xattr_flush_name_prefixes(struct erofs_sb_info *sbi)
 {
-	FILE *f = erofs_packedfile(sbi);
+	int fd = erofs_packedfile(sbi);
 	struct ea_type_node *tnode;
-	off_t offset;
+	s64 offset;
+	int err;
 
 	if (!ea_prefix_count)
 		return 0;
-	offset = ftello(f);
+	offset = lseek(fd, 0, SEEK_CUR);
 	if (offset < 0)
 		return -errno;
-	if (offset > UINT32_MAX)
-		return -EOVERFLOW;
-
 	offset = round_up(offset, 4);
-	if (fseek(f, offset, SEEK_SET))
+	if (lseek(fd, offset, SEEK_SET) < 0)
 		return -errno;
+
+	if ((offset >> 2) > UINT32_MAX)
+		return -EOVERFLOW;
 	sbi->xattr_prefix_start = (u32)offset >> 2;
 	sbi->xattr_prefix_count = ea_prefix_count;
 
@@ -846,10 +847,14 @@ int erofs_xattr_flush_name_prefixes(struct erofs_sb_info *sbi)
 		       infix_len);
 		len = sizeof(struct erofs_xattr_long_prefix) + infix_len;
 		u.s.size = cpu_to_le16(len);
-		if (fwrite(&u.s, sizeof(__le16) + len, 1, f) != 1)
+		err = __erofs_io_write(fd, &u.s, sizeof(__le16) + len);
+		if (err != sizeof(__le16) + len) {
+			if (err < 0)
+				return -errno;
 			return -EIO;
+		}
 		offset = round_up(offset + sizeof(__le16) + len, 4);
-		if (fseek(f, offset, SEEK_SET))
+		if (lseek(fd, offset, SEEK_SET) < 0)
 			return -errno;
 	}
 	erofs_sb_set_fragments(sbi);
-- 
2.43.5



More information about the Linux-erofs mailing list