:i	static char d_rename_sccs_id[] = "%W% %G%";
:1#include "V.h"
#include "sys_stat.h"
/*
* The rename() system call has some serious problems on many Unix releases. We
* try to detect the problems and correct for them, because some major problems
* with garbaged files have arisen due to failures in rename().  Note that this
* module  includes  the bfrename(src,dst) function, which you may wish to call
* directly, and avoid all the overhead of our sanity checking.
*/
static char m_hexargs  [] = "src=%08X dst=%08X";
static char m_nounlink [] = "%s\tCan't unlink \"%s\" [Err %d=%s=%s]";
static char m_renargs  [] = "\"%s\" as \"%s\"";
static char m_renamefr [] = "### rename from \"%s\"";
static char m_renameto [] = "### rename  to  \"%s\"";
static char m_renamev  [] = "rename(\"%s\",\"%s\")=%d [Err %d=%s=%s]";
/*
* Here's the debug routine invoked by Rename(src,dst):
*/
d_rename(src,dst)
	char*src;
	char*dst;
{	int  v;
	STat st;
:f	Fenter("Rename");
:8	V8 m_hexargs,src,dst V;
:6	V6 m_renargs,src,dst V;

	errno = 0;
#if defined(USE_syscall) && defined(SYS_rename)
	v = syscall(SYS_rename,src,dst);
#else
	v = rename(src,dst);
#endif
/*
* Part of the problem is that rename()often reports  success  when  if
* fact  it  didn't do the job at all.  Here's some code to try to spot
* this problem and maybe do something about it:
*/
:2	if (v == 0) {	/* Did it claim success? */
:2		if (Stat(src,&st) == 0) {
:2			V1 "### rename() succeeded, but %s still exists!",Dsps(src,-1) V;
:2			V1 m_renamefr,src V;
:2			V1 m_renameto,dst V;
:2			V1 "### trying brute-force rename ..." V;
:2			v = bfrename(src,dst);
:2		}
:2	} else {
:2		if (Stat(src,&st)) {
:2			V1 "src file \"%s\" doesn't exist [Err %d=%s=%s]",src,Errinfo V;
:2		} else {
:2			V1 "src file \"%s\" exists after failure.",src V;
:2		}
:2		if (Stat(dst,&st)) {
:2			V1 "dst file \"%s\" doesn't exist [Err %d=%s=%s]",dst,Errinfo V;
:2		} else {
:2			V1 "dst file \"%s\" exists after failure.",dst V;
:2		}
:6		H6(src,strlen(src)+1,"src");
:6		H6(dst,strlen(dst)+1,"dst");
:2	}
:6	V6 m_renamev,src,dst,v,Errlist V;
:f	Fexit;
	return v;
}
/*
* "Brute-force rename".  This is a routine that should work anywhere (assuming
* that  we  have  all  the requisite permissions), but it has the problem of a
* window in which the dst file doesn't exist.
*/
bfrename(src,dst)
	char*src;
	char*dst;
{	int  v=0;
	int  e=0;
	STat st;
:f	Fenter("bfrename");
:8	V8 m_hexargs,src,dst D;
:6	V6 m_renargs,src,dst D;
	if (v = unlink(dst)) {	/* Try to get rid of the dst file */
		e = errno;
:6		V6 m_nounlink,pname,dst,Errlist D;
	}
/*
* Here's the start of the window during which dst doesn't  exist.   Of
* course, dst may not have existed when we were called, but that's not
* our problem.
*/
	if (Stat(dst,&st) == 0) {	/* Does the dst file exist now? */
:2		errno = e;
:2		V3 "### unlink \"%s\" failed [Err %d=%s=%s]",dst,Errlist D;
		if (v == 0) {	/* ... but unlink() said it succeeded! */
:2			V1 "### unlink succeeded but file still exists!" D;
:2			V1 "### unlink \"%s\"",dst D;
:2			V1 "### Returning -1 with errno=EIO=%d.",EIO D;
			v = -1;
			e = EIO;	/* Is there a better code to return? */
		}
		Fail;	/* The dst file can't be unlinked */
	}
	if (v = link(src,dst)) {
		e = errno;
:6		V6 "%s\tCan't link \"%s\" to \"%s\" [Err %d=%s=%s]",pname,src,dst,Errlist D;
		Fail;
	}
/*
* The dst-less window should now be ended, assuming that  we  actually
* had  write  permission  in  dst's parent directory.  But then, if we
* didn't, we would have failed to unlink dst.
*/
	if (v = unlink(src)) {
		e = errno;
:6		V6 m_nounlink,pname,src,Errlist D;
		Fail;
	}
:2	if (v == 0) {	/* That appears to have succeeded */
:2		if (Stat(src,&st) == 0) {	/* Did it really? */
:2			V1 "### Brute-force rename didn't work!" D;
:2			V1 m_renamefr,src D;
:2			V1 m_renameto,dst D;
:2			V1 "### Returning -1 with errno=EIO=%d.",EIO D;
:2			v = -1;
:2			e = EIO;	/* This sort of makes sense */
:2		}
:2	}
fail:
:f	Fexit;
	errno = e;
	return v;
}
#if defined(ESIX3) || (defined(USE_syscall) && defined(SYS_rename))
/*
* ESIX Sys/VR3 (and probably many  others)  doesn't  even  have  the  rename()
* system call, so we fake it.  Note that we first unlink the destination file,
* because the link() call will fail if its 2nd arg exists.  Also,  on  Sys/VR4
* and Sun Solaris, there is a very serious bug in the library rename(). If src
* and dst are links to the same file, rename(src,dst) returns  0,  with  errno
* unchanged, but the rename didn't happen. Both files still exist. If you then
* write over src, you will also be writing over dst.  Sigh.
*/
rename(src,dst)
	char*src;
	char*dst;
{	int  v;
:f	Fenter("rename.b");
:8	V8 m_hexargs,src,dst D;
:6	V6 m_renargs,src,dst D;

	v = bfrename(src,dst);

:5	V5 m_renamev,src,dst,v,Errlist D;
:f	Fexit;
	return v;
}
#endif
