1/* move.c 2 Move a file. 3 4 Copyright (C) 1991, 1992, 1993 Ian Lance Taylor 5 6 This file is part of the Taylor UUCP package. 7 8 This program is free software; you can redistribute it and/or 9 modify it under the terms of the GNU General Public License as 10 published by the Free Software Foundation; either version 2 of the 11 License, or (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, but 14 WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. 21 22 The author of the program may be contacted at ian@airs.com. 23 */ 24 25#include "uucp.h" 26 27#include "uudefs.h" 28#include "sysdep.h" 29#include "system.h" 30 31#include <errno.h> 32 33#if HAVE_FCNTL_H 34#include <fcntl.h> 35#else 36#if HAVE_SYS_FILE_H 37#include <sys/file.h> 38#endif 39#endif 40 41/* Move (rename) a file from one name to another. This routine will 42 optionally create necessary directories, and fpublic indicates 43 whether the new directories should be publically accessible or not. 44 If fcheck is true, it will try to determine whether the named user 45 has write access to the new file. */ 46 47boolean 48fsysdep_move_file (zorig, zto, fmkdirs, fpublic, fcheck, zuser) 49 const char *zorig; 50 const char *zto; 51 boolean fmkdirs; 52 boolean fpublic; 53 boolean fcheck; 54 const char *zuser; 55{ 56 struct stat s; 57 int o; 58 59 DEBUG_MESSAGE2 (DEBUG_SPOOLDIR, 60 "fsysdep_move_file: Moving %s to %s", zorig, zto); 61 62 /* Optionally make sure that zuser has write access on the 63 directory. */ 64 if (fcheck) 65 { 66 char *zcopy; 67 char *zslash; 68 69 zcopy = zbufcpy (zto); 70 zslash = strrchr (zcopy, '/'); 71 if (zslash == zcopy) 72 zslash[1] = '\0'; 73 else 74 *zslash = '\0'; 75 76 if (stat (zcopy, &s) != 0) 77 { 78 ulog (LOG_ERROR, "stat (%s): %s", zcopy, strerror (errno)); 79 ubuffree (zcopy); 80 return FALSE; 81 } 82 if (! fsuser_access (&s, W_OK, zuser)) 83 { 84 ulog (LOG_ERROR, "%s: %s", zcopy, strerror (EACCES)); 85 ubuffree (zcopy); 86 return FALSE; 87 } 88 ubuffree (zcopy); 89 90 /* A malicious user now has a few milliseconds to change a 91 symbolic link to a directory uucp has write permission on but 92 the user does not (the obvious choice being /usr/lib/uucp). 93 The only certain method I can come up with to close this race 94 is to fork an suid process which takes on the users identity 95 and does the actual copy. This is sufficiently high overhead 96 that I'm not going to do it. */ 97 } 98 99 /* We try to use rename to move the file. */ 100 101 if (rename (zorig, zto) == 0) 102 return TRUE; 103 104 if (fmkdirs && errno == ENOENT) 105 { 106 if (! fsysdep_make_dirs (zto, fpublic)) 107 return FALSE; 108 if (rename (zorig, zto) == 0) 109 return TRUE; 110 } 111 112#if HAVE_RENAME 113 /* On some systems the system call rename seems to fail for 114 arbitrary reasons. To get around this, we always try to copy the 115 file by hand if the rename failed. */ 116 errno = EXDEV; 117#endif 118 119 /* If we can't link across devices, we must copy the file by hand. */ 120 if (errno != EXDEV) 121 { 122 ulog (LOG_ERROR, "rename (%s, %s): %s", zorig, zto, 123 strerror (errno)); 124 return FALSE; 125 } 126 127 /* Copy the file. */ 128 if (stat ((char *) zorig, &s) < 0) 129 { 130 ulog (LOG_ERROR, "stat (%s): %s", zorig, strerror (errno)); 131 return FALSE; 132 } 133 134 /* Make sure the file gets the right mode by creating it before we 135 call fcopy_file. */ 136 (void) remove (zto); 137 o = creat ((char *) zto, s.st_mode); 138 if (o < 0) 139 { 140 if (fmkdirs && errno == ENOENT) 141 { 142 if (! fsysdep_make_dirs (zto, fpublic)) 143 return FALSE; 144 o = creat ((char *) zto, s.st_mode); 145 } 146 if (o < 0) 147 { 148 ulog (LOG_ERROR, "creat during move (%s): %s", zto, strerror (errno)); 149 return FALSE; 150 } 151 } 152 (void) close (o); 153 154 if (! fcopy_file (zorig, zto, fpublic, fmkdirs, FALSE)) 155 return FALSE; 156 157 if (remove (zorig) != 0) 158 ulog (LOG_ERROR, "remove (%s): %s", zorig, strerror (errno)); 159 160 return TRUE; 161} 162