summaryrefslogtreecommitdiffstats
path: root/contrib/cvs/src/filesubr.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/cvs/src/filesubr.c')
-rw-r--r--contrib/cvs/src/filesubr.c205
1 files changed, 161 insertions, 44 deletions
diff --git a/contrib/cvs/src/filesubr.c b/contrib/cvs/src/filesubr.c
index b5121a7..0ccc3df 100644
--- a/contrib/cvs/src/filesubr.c
+++ b/contrib/cvs/src/filesubr.c
@@ -43,46 +43,66 @@ copy_file (from, to)
if (noexec)
return;
- if ((fdin = open (from, O_RDONLY)) < 0)
- error (1, errno, "cannot open %s for copying", from);
- if (fstat (fdin, &sb) < 0)
- error (1, errno, "cannot fstat %s", from);
- if ((fdout = creat (to, (int) sb.st_mode & 07777)) < 0)
- error (1, errno, "cannot create %s for copying", to);
- if (sb.st_size > 0)
+ /* If the file to be copied is a link or a device, then just create
+ the new link or device appropriately. */
+ if (islink (from))
{
- char buf[BUFSIZ];
- int n;
+ char *source = xreadlink (from);
+ symlink (source, to);
+ free (source);
+ return;
+ }
- for (;;)
+ if (isdevice (from))
+ {
+ if (stat (from, &sb) < 0)
+ error (1, errno, "cannot stat %s", from);
+ mknod (to, sb.st_mode, sb.st_rdev);
+ }
+ else
+ {
+ /* Not a link or a device... probably a regular file. */
+ if ((fdin = open (from, O_RDONLY)) < 0)
+ error (1, errno, "cannot open %s for copying", from);
+ if (fstat (fdin, &sb) < 0)
+ error (1, errno, "cannot fstat %s", from);
+ if ((fdout = creat (to, (int) sb.st_mode & 07777)) < 0)
+ error (1, errno, "cannot create %s for copying", to);
+ if (sb.st_size > 0)
{
- n = read (fdin, buf, sizeof(buf));
- if (n == -1)
+ char buf[BUFSIZ];
+ int n;
+
+ for (;;)
{
+ n = read (fdin, buf, sizeof(buf));
+ if (n == -1)
+ {
#ifdef EINTR
- if (errno == EINTR)
- continue;
+ if (errno == EINTR)
+ continue;
#endif
- error (1, errno, "cannot read file %s for copying", from);
- }
- else if (n == 0)
- break;
-
- if (write(fdout, buf, n) != n) {
- error (1, errno, "cannot write file %s for copying", to);
+ error (1, errno, "cannot read file %s for copying", from);
+ }
+ else if (n == 0)
+ break;
+
+ if (write(fdout, buf, n) != n) {
+ error (1, errno, "cannot write file %s for copying", to);
+ }
}
- }
#ifdef HAVE_FSYNC
- if (fsync (fdout))
- error (1, errno, "cannot fsync file %s after copying", to);
+ if (fsync (fdout))
+ error (1, errno, "cannot fsync file %s after copying", to);
#endif
- }
+ }
- if (close (fdin) < 0)
- error (0, errno, "cannot close %s", from);
- if (close (fdout) < 0)
- error (1, errno, "cannot close %s", to);
+ if (close (fdin) < 0)
+ error (0, errno, "cannot close %s", from);
+ if (close (fdout) < 0)
+ error (1, errno, "cannot close %s", to);
+ }
/* now, set the times for the copied file to match those of the original */
memset ((char *) &t, 0, sizeof (t));
@@ -119,7 +139,7 @@ islink (file)
#ifdef S_ISLNK
struct stat sb;
- if (lstat (file, &sb) < 0)
+ if (CVS_LSTAT (file, &sb) < 0)
return (0);
return (S_ISLNK (sb.st_mode));
#else
@@ -128,6 +148,29 @@ islink (file)
}
/*
+ * Returns non-zero if the argument file is a block or
+ * character special device.
+ */
+int
+isdevice (file)
+ const char *file;
+{
+ struct stat sb;
+
+ if (CVS_LSTAT (file, &sb) < 0)
+ return (0);
+#ifdef S_ISBLK
+ if (S_ISBLK (sb.st_mode))
+ return 1;
+#endif
+#ifdef S_ISCHR
+ if (S_ISCHR (sb.st_mode))
+ return 1;
+#endif
+ return 0;
+}
+
+/*
* Returns non-zero if the argument file exists.
*/
int
@@ -298,6 +341,9 @@ mkdir_if_needed (name)
/*
* Change the mode of a file, either adding write permissions, or removing
* all write permissions. Either change honors the current umask setting.
+ *
+ * Don't do anything if PreservePermissions is set to `yes'. This may
+ * have unexpected consequences for some uses of xchmod.
*/
void
xchmod (fname, writable)
@@ -307,6 +353,9 @@ xchmod (fname, writable)
struct stat sb;
mode_t mode, oumask;
+ if (preserve_perms)
+ return;
+
if (stat (fname, &sb) < 0)
{
if (!noexec)
@@ -417,6 +466,8 @@ int
unlink_file_dir (f)
const char *f;
{
+ struct stat sb;
+
if (trace
#ifdef SERVER_SUPPORT
/* This is called by the server parent process in contexts where
@@ -433,20 +484,23 @@ unlink_file_dir (f)
/* For at least some unices, if root tries to unlink() a directory,
instead of doing something rational like returning EISDIR,
the system will gleefully go ahead and corrupt the filesystem.
- So we first call isdir() to see if it is OK to call unlink(). This
+ So we first call stat() to see if it is OK to call unlink(). This
doesn't quite work--if someone creates a directory between the
- call to isdir() and the call to unlink(), we'll still corrupt
+ call to stat() and the call to unlink(), we'll still corrupt
the filesystem. Where is the Unix Haters Handbook when you need
it? */
- if (isdir(f))
- return deep_remove_dir(f);
- else
+ if (stat (f, &sb) < 0)
{
- if (unlink (f) != 0)
+ if (existence_error (errno))
+ {
+ /* The file or directory doesn't exist anyhow. */
return -1;
+ }
}
- /* We were able to remove the file from the disk */
- return 0;
+ else if (S_ISDIR (sb.st_mode))
+ return deep_remove_dir (f);
+
+ return unlink (f);
}
/* Remove a directory and everything it contains. Returns 0 for
@@ -557,6 +611,8 @@ block_read (fd, buf, nchars)
/*
* Compare "file1" to "file2". Return non-zero if they don't compare exactly.
+ * If FILE1 and FILE2 are special files, compare their salient characteristics
+ * (i.e. major/minor device numbers, links, etc.
*/
int
xcmp (file1, file2)
@@ -568,14 +624,42 @@ xcmp (file1, file2)
int fd1, fd2;
int ret;
+ if (CVS_LSTAT (file1, &sb1) < 0)
+ error (1, errno, "cannot lstat %s", file1);
+ if (CVS_LSTAT (file2, &sb2) < 0)
+ error (1, errno, "cannot lstat %s", file2);
+
+ /* If FILE1 and FILE2 are not the same file type, they are unequal. */
+ if ((sb1.st_mode & S_IFMT) != (sb2.st_mode & S_IFMT))
+ return 1;
+
+ /* If FILE1 and FILE2 are symlinks, they are equal if they point to
+ the same thing. */
+ if (S_ISLNK (sb1.st_mode) && S_ISLNK (sb2.st_mode))
+ {
+ int result;
+ buf1 = xreadlink (file1);
+ buf2 = xreadlink (file2);
+ result = (strcmp (buf1, buf2) == 0);
+ free (buf1);
+ free (buf2);
+ return result;
+ }
+
+ /* If FILE1 and FILE2 are devices, they are equal if their device
+ numbers match. */
+ if (S_ISBLK (sb1.st_mode) || S_ISCHR (sb1.st_mode))
+ {
+ if (sb1.st_rdev == sb2.st_rdev)
+ return 0;
+ else
+ return 1;
+ }
+
if ((fd1 = open (file1, O_RDONLY)) < 0)
error (1, errno, "cannot open file %s for comparing", file1);
if ((fd2 = open (file2, O_RDONLY)) < 0)
error (1, errno, "cannot open file %s for comparing", file2);
- if (fstat (fd1, &sb1) < 0)
- error (1, errno, "cannot fstat %s", file1);
- if (fstat (fd2, &sb2) < 0)
- error (1, errno, "cannot fstat %s", file2);
/* A generic file compare routine might compare st_dev & st_ino here
to see if the two files being compared are actually the same file.
@@ -677,6 +761,39 @@ isabsolute (filename)
return filename[0] == '/';
}
+/*
+ * Return a string (dynamically allocated) with the name of the file to which
+ * LINK is symlinked.
+ */
+char *
+xreadlink (link)
+ const char *link;
+{
+ char *file = NULL;
+ int buflen = BUFSIZ;
+
+ if (!islink (link))
+ return NULL;
+
+ /* Get the name of the file to which `from' is linked.
+ FIXME: what portability issues arise here? Are readlink &
+ ENAMETOOLONG defined on all systems? -twp */
+ do
+ {
+ file = xrealloc (file, buflen);
+ errno = 0;
+ readlink (link, file, buflen);
+ buflen *= 2;
+ }
+ while (errno == ENAMETOOLONG);
+
+ if (errno)
+ error (1, errno, "cannot readlink %s", link);
+
+ return file;
+}
+
+
/* Return a pointer into PATH's last component. */
char *
@@ -684,8 +801,8 @@ last_component (path)
char *path;
{
char *last = strrchr (path, '/');
-
- if (last)
+
+ if (last && (last != path))
return last + 1;
else
return path;
OpenPOWER on IntegriCloud