Blame src/arbits/slbt_archive_store.c

c8d21a
/*******************************************************************/
c8d21a
/*  slibtool: a skinny libtool implementation, written in C        */
c8d21a
/*  Copyright (C) 2016--2024  SysDeer Technologies, LLC            */
c8d21a
/*  Released under the Standard MIT License; see COPYING.SLIBTOOL. */
c8d21a
/*******************************************************************/
c8d21a
c8d21a
#include <time.h>
c8d21a
#include <fcntl.h>
c8d21a
#include <stdio.h>
c8d21a
#include <stddef.h>
c8d21a
#include <limits.h>
c8d21a
#include <string.h>
c8d21a
#include <stdlib.h>
c8d21a
#include <unistd.h>
c8d21a
#include <inttypes.h>
c8d21a
#include <sys/stat.h>
c8d21a
c8d21a
#include <slibtool/slibtool.h>
c8d21a
#include <slibtool/slibtool_arbits.h>
c8d21a
#include "slibtool_ar_impl.h"
c8d21a
#include "slibtool_driver_impl.h"
c8d21a
#include "slibtool_errinfo_impl.h"
c8d21a
c8d21a
/****************************************************/
c8d21a
/* As elsewhere in slibtool, file-system operations */
c8d21a
/* utilizie the _at variants of the relevant posix  */
c8d21a
/* interfaces. In the case of archives, that means  */
c8d21a
/* passing dctx->fdctx->fdcwd as the _fdat_ param,  */
c8d21a
/* where dctx is the driver context which was used  */
c8d21a
/* with slbt_get_archive_ctx().                     */
c8d21a
/************************************************** */
c8d21a
c8d21a
#define PPRIX64 "%"PRIx64
c8d21a
c8d21a
int slbt_store_archive(
c8d21a
	struct slbt_archive_ctx * arctx,
c8d21a
	const char *              path,
c8d21a
	mode_t                    mode)
c8d21a
{
c8d21a
	const struct slbt_driver_ctx *  dctx;
c8d21a
	struct stat                     st;
c8d21a
	int                             fdat;
c8d21a
	int                             fdtmp;
c8d21a
	char *                          mark;
c8d21a
	char *                          slash;
c8d21a
	size_t                          buflen;
c8d21a
	size_t                          nbytes;
c8d21a
	ssize_t                         written;
c8d21a
	char                            buf[PATH_MAX];
c8d21a
c8d21a
	/* init dctx */
c8d21a
	if (!(dctx = slbt_get_archive_ictx(arctx)->dctx))
c8d21a
		return -1;
c8d21a
c8d21a
	/* validation */
c8d21a
	if (strlen(path) >= PATH_MAX)
c8d21a
		return SLBT_CUSTOM_ERROR(
c8d21a
			dctx,
c8d21a
			SLBT_ERR_FLOW_ERROR);
c8d21a
c8d21a
	/**************************************************/
c8d21a
	/* create temporary file in the target directory  */
c8d21a
	/*                                                */
c8d21a
	/* the tmpfile name pattern involes the inode     */
c8d21a
	/* of the target directory, a local stack address */
c8d21a
	/* in the calling thread, the current time, and   */
c8d21a
	/* finally the pid of the current process.        */
c8d21a
	/**************************************************/
c8d21a
c8d21a
	memset(buf,0,sizeof(buf));
c8d21a
	strcpy(buf,path);
c8d21a
c8d21a
	fdat = slbt_driver_fdcwd(dctx);
c8d21a
c8d21a
	mark = (slash = strrchr(buf,'/'))
c8d21a
		? slash : buf;
c8d21a
c8d21a
	if (slash) {
c8d21a
		*++mark = '\0';
c8d21a
c8d21a
		if (fstatat(fdat,buf,&st,0) < 0)
c8d21a
			return SLBT_SYSTEM_ERROR(
c8d21a
				dctx,0);
c8d21a
	} else {
c8d21a
		if (fstatat(fdat,".",&st,0) < 0)
c8d21a
			return SLBT_SYSTEM_ERROR(
c8d21a
				dctx,0);
c8d21a
	}
c8d21a
c8d21a
	buflen = sizeof(buf) - (mark - buf);
c8d21a
	nbytes = snprintf(
c8d21a
		mark,
c8d21a
		buflen,
c8d21a
		".slibtool.tmpfile"
c8d21a
		".inode."PPRIX64
c8d21a
		".time."PPRIX64
c8d21a
		".salt.%p"
c8d21a
		".pid.%d"
c8d21a
		".tmp",
c8d21a
		st.st_ino,
c8d21a
		time(0),&buf,
c8d21a
		getpid());
c8d21a
c8d21a
	if (nbytes >= buflen)
c8d21a
		return SLBT_CUSTOM_ERROR(
c8d21a
			dctx,
c8d21a
			SLBT_ERR_FLOW_ERROR);
c8d21a
c8d21a
	if ((fdtmp = openat(fdat,buf,O_WRONLY|O_CREAT|O_EXCL,mode)) < 0)
c8d21a
		return SLBT_SYSTEM_ERROR(dctx,0);
c8d21a
c8d21a
	/* set archive size */
c8d21a
	if (ftruncate(fdtmp,arctx->map->map_size) < 0)
c8d21a
		return SLBT_SYSTEM_ERROR(dctx,0);
c8d21a
c8d21a
	/* write archive */
c8d21a
	mark   = arctx->map->map_addr;
c8d21a
	nbytes = arctx->map->map_size;
c8d21a
c8d21a
	for (; nbytes; ) {
c8d21a
		written = write(fdtmp,mark,nbytes);
c8d21a
c8d21a
		while ((written < 0) && (errno == EINTR))
c8d21a
			written = write(fdtmp,mark,nbytes);
c8d21a
c8d21a
		if (written < 0) {
c8d21a
			unlinkat(fdat,buf,0);
c8d21a
			return SLBT_SYSTEM_ERROR(dctx,0);
c8d21a
		};
c8d21a
c8d21a
		nbytes -= written;
c8d21a
		mark   += written;
c8d21a
	}
c8d21a
c8d21a
	/* finalize (atomically) */
c8d21a
	if (renameat(fdat,buf,fdat,path) < 0) {
c8d21a
		unlinkat(fdat,buf,0);
c8d21a
		return SLBT_SYSTEM_ERROR(dctx,0);
c8d21a
	}
c8d21a
c8d21a
	/* yay */
c8d21a
	return 0;
c8d21a
}