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