aboutsummaryrefslogtreecommitdiff
path: root/gnu/libexec/uucp/libunix/move.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnu/libexec/uucp/libunix/move.c')
-rw-r--r--gnu/libexec/uucp/libunix/move.c176
1 files changed, 176 insertions, 0 deletions
diff --git a/gnu/libexec/uucp/libunix/move.c b/gnu/libexec/uucp/libunix/move.c
new file mode 100644
index 000000000000..ccfe6d4d728d
--- /dev/null
+++ b/gnu/libexec/uucp/libunix/move.c
@@ -0,0 +1,176 @@
+/* move.c
+ Move a file.
+
+ Copyright (C) 1991, 1992 Ian Lance Taylor
+
+ This file is part of the Taylor UUCP package.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ The author of the program may be contacted at ian@airs.com or
+ c/o Infinity Development Systems, P.O. Box 520, Waltham, MA 02254.
+ */
+
+#include "uucp.h"
+
+#include "uudefs.h"
+#include "sysdep.h"
+#include "system.h"
+
+#include <errno.h>
+
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#else
+#if HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+#endif
+
+/* Move (rename) a file from one name to another. This routine will
+ optionally create necessary directories, and fpublic indicates
+ whether the new directories should be publically accessible or not.
+ If fcheck is true, it will try to determine whether the named user
+ has write access to the new file. */
+
+boolean
+fsysdep_move_file (zorig, zto, fmkdirs, fpublic, fcheck, zuser)
+ const char *zorig;
+ const char *zto;
+ boolean fmkdirs;
+ boolean fpublic;
+ boolean fcheck;
+ const char *zuser;
+{
+ struct stat s;
+ int o;
+
+ DEBUG_MESSAGE2 (DEBUG_SPOOLDIR,
+ "fsysdep_move_file: Moving %s to %s", zorig, zto);
+
+ /* Optionally make sure that zuser has write access on the
+ directory. */
+ if (fcheck)
+ {
+ char *zcopy;
+ char *zslash;
+
+ zcopy = zbufcpy (zto);
+ zslash = strrchr (zcopy, '/');
+ if (zslash == zcopy)
+ zslash[1] = '\0';
+ else
+ *zslash = '\0';
+
+ if (stat (zcopy, &s) != 0)
+ {
+ ulog (LOG_ERROR, "stat (%s): %s", zcopy, strerror (errno));
+ (void) remove (zorig);
+ ubuffree (zcopy);
+ return FALSE;
+ }
+ if (! fsuser_access (&s, W_OK, zuser))
+ {
+ ulog (LOG_ERROR, "%s: %s", zcopy, strerror (EACCES));
+ (void) remove (zorig);
+ ubuffree (zcopy);
+ return FALSE;
+ }
+ ubuffree (zcopy);
+
+ /* A malicious user now has a few milliseconds to change a
+ symbolic link to a directory uucp has write permission on but
+ the user does not (the obvious choice being /usr/lib/uucp).
+ The only certain method I can come up with to close this race
+ is to fork an suid process which takes on the users identity
+ and does the actual copy. This is sufficiently high overhead
+ that I'm not going to do it. */
+ }
+
+ /* We try to use rename to move the file. */
+
+ if (rename (zorig, zto) == 0)
+ return TRUE;
+
+ if (fmkdirs && errno == ENOENT)
+ {
+ if (! fsysdep_make_dirs (zto, fpublic))
+ {
+ (void) remove (zorig);
+ return FALSE;
+ }
+ if (rename (zorig, zto) == 0)
+ return TRUE;
+ }
+
+#if HAVE_RENAME
+ /* On some systems the system call rename seems to fail for
+ arbitrary reasons. To get around this, we always try to copy the
+ file by hand if the rename failed. */
+ errno = EXDEV;
+#endif
+
+ /* If we can't link across devices, we must copy the file by hand. */
+ if (errno != EXDEV)
+ {
+ ulog (LOG_ERROR, "rename (%s, %s): %s", zorig, zto,
+ strerror (errno));
+ (void) remove (zorig);
+ return FALSE;
+ }
+
+ /* Copy the file. */
+ if (stat ((char *) zorig, &s) < 0)
+ {
+ ulog (LOG_ERROR, "stat (%s): %s", zorig, strerror (errno));
+ (void) remove (zorig);
+ return FALSE;
+ }
+
+ /* Make sure the file gets the right mode by creating it before we
+ call fcopy_file. */
+ (void) remove (zto);
+ o = creat ((char *) zto, s.st_mode);
+ if (o < 0)
+ {
+ if (fmkdirs && errno == ENOENT)
+ {
+ if (! fsysdep_make_dirs (zto, fpublic))
+ {
+ (void) remove (zorig);
+ return FALSE;
+ }
+ o = creat ((char *) zto, s.st_mode);
+ }
+ if (o < 0)
+ {
+ ulog (LOG_ERROR, "creat (%s): %s", zto, strerror (errno));
+ (void) remove (zorig);
+ return FALSE;
+ }
+ }
+ (void) close (o);
+
+ if (! fcopy_file (zorig, zto, fpublic, fmkdirs))
+ {
+ (void) remove (zorig);
+ return FALSE;
+ }
+
+ if (remove (zorig) != 0)
+ ulog (LOG_ERROR, "remove (%s): %s", zorig, strerror (errno));
+
+ return TRUE;
+}