117721Speter/* filesubr.c --- subroutines for dealing with files 217721Speter Jim Blandy <jimb@cyclic.com> 317721Speter 417721Speter This file is part of GNU CVS. 517721Speter 617721Speter GNU CVS is free software; you can redistribute it and/or modify it 717721Speter under the terms of the GNU General Public License as published by the 817721Speter Free Software Foundation; either version 2, or (at your option) any 917721Speter later version. 1017721Speter 1117721Speter This program is distributed in the hope that it will be useful, 1217721Speter but WITHOUT ANY WARRANTY; without even the implied warranty of 1317721Speter MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1425839Speter GNU General Public License for more details. */ 1517721Speter 1617721Speter/* These functions were moved out of subr.c because they need different 1717721Speter definitions under operating systems (like, say, Windows NT) with different 1817721Speter file system semantics. */ 1917721Speter 2081407Speter#include <assert.h> 2117721Speter#include "cvs.h" 2217721Speter 23175269Sobrien#include "xsize.h" 24175269Sobrien 2517721Speterstatic int deep_remove_dir PROTO((const char *path)); 2617721Speter 2717721Speter/* 2817721Speter * Copies "from" to "to". 2917721Speter */ 3017721Spetervoid 3117721Spetercopy_file (from, to) 3217721Speter const char *from; 3317721Speter const char *to; 3417721Speter{ 3517721Speter struct stat sb; 3617721Speter struct utimbuf t; 3717721Speter int fdin, fdout; 3817721Speter 3917721Speter if (trace) 4054431Speter (void) fprintf (stderr, "%s-> copy(%s,%s)\n", 4154431Speter CLIENT_SERVER_STR, from, to); 4217721Speter if (noexec) 4317721Speter return; 4417721Speter 4534461Speter /* If the file to be copied is a link or a device, then just create 4634461Speter the new link or device appropriately. */ 4734461Speter if (islink (from)) 4817721Speter { 4934461Speter char *source = xreadlink (from); 5034461Speter symlink (source, to); 5134461Speter free (source); 5234461Speter return; 5334461Speter } 5417721Speter 5534461Speter if (isdevice (from)) 5634461Speter { 57107487Speter#if defined(HAVE_MKNOD) && defined(HAVE_STRUCT_STAT_ST_RDEV) 5834461Speter if (stat (from, &sb) < 0) 5934461Speter error (1, errno, "cannot stat %s", from); 6034461Speter mknod (to, sb.st_mode, sb.st_rdev); 6166528Speter#else 6266528Speter error (1, 0, "cannot copy device files on this system (%s)", from); 6366528Speter#endif 6434461Speter } 6534461Speter else 6634461Speter { 6734461Speter /* Not a link or a device... probably a regular file. */ 6834461Speter if ((fdin = open (from, O_RDONLY)) < 0) 6934461Speter error (1, errno, "cannot open %s for copying", from); 7034461Speter if (fstat (fdin, &sb) < 0) 7134461Speter error (1, errno, "cannot fstat %s", from); 7234461Speter if ((fdout = creat (to, (int) sb.st_mode & 07777)) < 0) 7334461Speter error (1, errno, "cannot create %s for copying", to); 7434461Speter if (sb.st_size > 0) 7517721Speter { 7634461Speter char buf[BUFSIZ]; 7734461Speter int n; 7834461Speter 7934461Speter for (;;) 8017721Speter { 8134461Speter n = read (fdin, buf, sizeof(buf)); 8234461Speter if (n == -1) 8334461Speter { 8417721Speter#ifdef EINTR 8534461Speter if (errno == EINTR) 8634461Speter continue; 8717721Speter#endif 8834461Speter error (1, errno, "cannot read file %s for copying", from); 8934461Speter } 9034461Speter else if (n == 0) 9134461Speter break; 9234461Speter 9334461Speter if (write(fdout, buf, n) != n) { 9434461Speter error (1, errno, "cannot write file %s for copying", to); 9534461Speter } 9617721Speter } 9717721Speter 9817721Speter#ifdef HAVE_FSYNC 9934461Speter if (fsync (fdout)) 10034461Speter error (1, errno, "cannot fsync file %s after copying", to); 10117721Speter#endif 10234461Speter } 10334461Speter 10434461Speter if (close (fdin) < 0) 10534461Speter error (0, errno, "cannot close %s", from); 10634461Speter if (close (fdout) < 0) 10734461Speter error (1, errno, "cannot close %s", to); 10817721Speter } 10917721Speter 110175269Sobrien /* preserve last access & modification times */ 11117721Speter memset ((char *) &t, 0, sizeof (t)); 11217721Speter t.actime = sb.st_atime; 11317721Speter t.modtime = sb.st_mtime; 11417721Speter (void) utime (to, &t); 11517721Speter} 11617721Speter 11717721Speter/* FIXME-krp: these functions would benefit from caching the char * & 11817721Speter stat buf. */ 11917721Speter 12017721Speter/* 12117721Speter * Returns non-zero if the argument file is a directory, or is a symbolic 12217721Speter * link which points to a directory. 12317721Speter */ 12417721Speterint 12517721Speterisdir (file) 12617721Speter const char *file; 12717721Speter{ 12817721Speter struct stat sb; 12917721Speter 13017721Speter if (stat (file, &sb) < 0) 13117721Speter return (0); 13217721Speter return (S_ISDIR (sb.st_mode)); 13317721Speter} 13417721Speter 13517721Speter/* 13617721Speter * Returns non-zero if the argument file is a symbolic link. 13717721Speter */ 13817721Speterint 13917721Speterislink (file) 14017721Speter const char *file; 14117721Speter{ 14217721Speter#ifdef S_ISLNK 14317721Speter struct stat sb; 14417721Speter 14534461Speter if (CVS_LSTAT (file, &sb) < 0) 14617721Speter return (0); 14717721Speter return (S_ISLNK (sb.st_mode)); 14817721Speter#else 14917721Speter return (0); 15017721Speter#endif 15117721Speter} 15217721Speter 15317721Speter/* 15434461Speter * Returns non-zero if the argument file is a block or 15534461Speter * character special device. 15634461Speter */ 15734461Speterint 15834461Speterisdevice (file) 15934461Speter const char *file; 16034461Speter{ 16134461Speter struct stat sb; 16234461Speter 16334461Speter if (CVS_LSTAT (file, &sb) < 0) 16434461Speter return (0); 16534461Speter#ifdef S_ISBLK 16634461Speter if (S_ISBLK (sb.st_mode)) 16734461Speter return 1; 16834461Speter#endif 16934461Speter#ifdef S_ISCHR 17034461Speter if (S_ISCHR (sb.st_mode)) 17134461Speter return 1; 17234461Speter#endif 17334461Speter return 0; 17434461Speter} 17534461Speter 17634461Speter/* 17717721Speter * Returns non-zero if the argument file exists. 17817721Speter */ 17917721Speterint 18017721Speterisfile (file) 18117721Speter const char *file; 18217721Speter{ 18317721Speter return isaccessible(file, F_OK); 18417721Speter} 18517721Speter 18617721Speter/* 18717721Speter * Returns non-zero if the argument file is readable. 18817721Speter */ 18917721Speterint 19017721Speterisreadable (file) 19117721Speter const char *file; 19217721Speter{ 19317721Speter return isaccessible(file, R_OK); 19417721Speter} 19517721Speter 19617721Speter/* 19717721Speter * Returns non-zero if the argument file is writable. 19817721Speter */ 19917721Speterint 20017721Speteriswritable (file) 20117721Speter const char *file; 20217721Speter{ 20317721Speter return isaccessible(file, W_OK); 20417721Speter} 20517721Speter 20617721Speter/* 20717721Speter * Returns non-zero if the argument file is accessable according to 20817721Speter * mode. If compiled with SETXID_SUPPORT also works if cvs has setxid 20917721Speter * bits set. 21017721Speter */ 21117721Speterint 21217721Speterisaccessible (file, mode) 21317721Speter const char *file; 21417721Speter const int mode; 21517721Speter{ 21617721Speter#ifdef SETXID_SUPPORT 21717721Speter struct stat sb; 21817721Speter int umask = 0; 21917721Speter int gmask = 0; 22017721Speter int omask = 0; 221107487Speter int uid, mask; 22217721Speter 22317721Speter if (stat(file, &sb) == -1) 22417721Speter return 0; 22517721Speter if (mode == F_OK) 22617721Speter return 1; 22717721Speter 22817721Speter uid = geteuid(); 22917721Speter if (uid == 0) /* superuser */ 23017721Speter { 231107487Speter if (!(mode & X_OK) || (sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))) 23217721Speter return 1; 233107487Speter 234107487Speter errno = EACCES; 235107487Speter return 0; 23617721Speter } 23717721Speter 23817721Speter if (mode & R_OK) 23917721Speter { 24017721Speter umask |= S_IRUSR; 24117721Speter gmask |= S_IRGRP; 24217721Speter omask |= S_IROTH; 24317721Speter } 24417721Speter if (mode & W_OK) 24517721Speter { 24617721Speter umask |= S_IWUSR; 24717721Speter gmask |= S_IWGRP; 24817721Speter omask |= S_IWOTH; 24917721Speter } 25017721Speter if (mode & X_OK) 25117721Speter { 25217721Speter umask |= S_IXUSR; 25317721Speter gmask |= S_IXGRP; 25417721Speter omask |= S_IXOTH; 25517721Speter } 25617721Speter 257107487Speter mask = sb.st_uid == uid ? umask : sb.st_gid == getegid() ? gmask : omask; 258107487Speter if ((sb.st_mode & mask) == mask) 259107487Speter return 1; 260107487Speter errno = EACCES; 261107487Speter return 0; 26217721Speter#else 26317721Speter return access(file, mode) == 0; 26417721Speter#endif 26517721Speter} 26617721Speter 26717721Speter/* 26817721Speter * Open a file and die if it fails 26917721Speter */ 27017721SpeterFILE * 27117721Speteropen_file (name, mode) 27217721Speter const char *name; 27317721Speter const char *mode; 27417721Speter{ 27517721Speter FILE *fp; 27617721Speter 27717721Speter if ((fp = fopen (name, mode)) == NULL) 27817721Speter error (1, errno, "cannot open %s", name); 27917721Speter return (fp); 28017721Speter} 28117721Speter 28217721Speter/* 28317721Speter * Make a directory and die if it fails 28417721Speter */ 28517721Spetervoid 28617721Spetermake_directory (name) 28717721Speter const char *name; 28817721Speter{ 28917721Speter struct stat sb; 29017721Speter 29117721Speter if (stat (name, &sb) == 0 && (!S_ISDIR (sb.st_mode))) 29217721Speter error (0, 0, "%s already exists but is not a directory", name); 29317721Speter if (!noexec && mkdir (name, 0777) < 0) 29417721Speter error (1, errno, "cannot make directory %s", name); 29517721Speter} 29617721Speter 29717721Speter/* 29817721Speter * Make a path to the argument directory, printing a message if something 29917721Speter * goes wrong. 30017721Speter */ 30117721Spetervoid 30217721Spetermake_directories (name) 30317721Speter const char *name; 30417721Speter{ 30517721Speter char *cp; 30617721Speter 30717721Speter if (noexec) 30817721Speter return; 30917721Speter 31017721Speter if (mkdir (name, 0777) == 0 || errno == EEXIST) 31117721Speter return; 31217721Speter if (! existence_error (errno)) 31317721Speter { 31417721Speter error (0, errno, "cannot make path to %s", name); 31517721Speter return; 31617721Speter } 31717721Speter if ((cp = strrchr (name, '/')) == NULL) 31817721Speter return; 31917721Speter *cp = '\0'; 32017721Speter make_directories (name); 32117721Speter *cp++ = '/'; 32217721Speter if (*cp == '\0') 32317721Speter return; 32417721Speter (void) mkdir (name, 0777); 32517721Speter} 32617721Speter 32725839Speter/* Create directory NAME if it does not already exist; fatal error for 32825839Speter other errors. Returns 0 if directory was created; 1 if it already 32925839Speter existed. */ 33025839Speterint 33125839Spetermkdir_if_needed (name) 332128269Speter const char *name; 33325839Speter{ 33425839Speter if (mkdir (name, 0777) < 0) 33525839Speter { 336128269Speter int save_errno = errno; 337128269Speter if (save_errno != EEXIST && !isdir (name)) 338128269Speter error (1, save_errno, "cannot make directory %s", name); 33925839Speter return 1; 34025839Speter } 34125839Speter return 0; 34225839Speter} 34325839Speter 34417721Speter/* 34517721Speter * Change the mode of a file, either adding write permissions, or removing 34617721Speter * all write permissions. Either change honors the current umask setting. 34734461Speter * 34834461Speter * Don't do anything if PreservePermissions is set to `yes'. This may 34934461Speter * have unexpected consequences for some uses of xchmod. 35017721Speter */ 35117721Spetervoid 35217721Speterxchmod (fname, writable) 353128269Speter const char *fname; 35417721Speter int writable; 35517721Speter{ 35617721Speter struct stat sb; 35717721Speter mode_t mode, oumask; 35817721Speter 35934461Speter if (preserve_perms) 36034461Speter return; 36134461Speter 36217721Speter if (stat (fname, &sb) < 0) 36317721Speter { 36417721Speter if (!noexec) 36517721Speter error (0, errno, "cannot stat %s", fname); 36617721Speter return; 36717721Speter } 36817721Speter oumask = umask (0); 36917721Speter (void) umask (oumask); 37017721Speter if (writable) 37117721Speter { 37217721Speter mode = sb.st_mode | (~oumask 37317721Speter & (((sb.st_mode & S_IRUSR) ? S_IWUSR : 0) 37417721Speter | ((sb.st_mode & S_IRGRP) ? S_IWGRP : 0) 37517721Speter | ((sb.st_mode & S_IROTH) ? S_IWOTH : 0))); 37617721Speter } 37717721Speter else 37817721Speter { 37917721Speter mode = sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH) & ~oumask; 38017721Speter } 38117721Speter 38217721Speter if (trace) 38354431Speter (void) fprintf (stderr, "%s-> chmod(%s,%o)\n", 38454431Speter CLIENT_SERVER_STR, fname, 38517721Speter (unsigned int) mode); 38617721Speter if (noexec) 38717721Speter return; 38817721Speter 38917721Speter if (chmod (fname, mode) < 0) 39017721Speter error (0, errno, "cannot change mode of file %s", fname); 39117721Speter} 39217721Speter 39317721Speter/* 39417721Speter * Rename a file and die if it fails 39517721Speter */ 39617721Spetervoid 39717721Speterrename_file (from, to) 39817721Speter const char *from; 39917721Speter const char *to; 40017721Speter{ 40117721Speter if (trace) 40254431Speter (void) fprintf (stderr, "%s-> rename(%s,%s)\n", 40354431Speter CLIENT_SERVER_STR, from, to); 40417721Speter if (noexec) 40517721Speter return; 40617721Speter 40717721Speter if (rename (from, to) < 0) 40817721Speter error (1, errno, "cannot rename file %s to %s", from, to); 40917721Speter} 41017721Speter 41117721Speter/* 41217721Speter * unlink a file, if possible. 41317721Speter */ 41417721Speterint 41517721Speterunlink_file (f) 41617721Speter const char *f; 41717721Speter{ 41817721Speter if (trace) 41981407Speter (void) fprintf (stderr, "%s-> unlink_file(%s)\n", 42054431Speter CLIENT_SERVER_STR, f); 42117721Speter if (noexec) 42217721Speter return (0); 42317721Speter 42481407Speter return (CVS_UNLINK (f)); 42517721Speter} 42617721Speter 42717721Speter/* 42817721Speter * Unlink a file or dir, if possible. If it is a directory do a deep 42917721Speter * removal of all of the files in the directory. Return -1 on error 43017721Speter * (in which case errno is set). 43117721Speter */ 43217721Speterint 43317721Speterunlink_file_dir (f) 43417721Speter const char *f; 43517721Speter{ 43634461Speter struct stat sb; 43734461Speter 438175269Sobrien /* This is called by the server parent process in contexts where 439175269Sobrien it is not OK to send output (e.g. after we sent "ok" to the 440175269Sobrien client). */ 441175269Sobrien if (trace && !server_active) 44217721Speter (void) fprintf (stderr, "-> unlink_file_dir(%s)\n", f); 44332785Speter 44417721Speter if (noexec) 44517721Speter return (0); 44617721Speter 44717721Speter /* For at least some unices, if root tries to unlink() a directory, 44817721Speter instead of doing something rational like returning EISDIR, 44917721Speter the system will gleefully go ahead and corrupt the filesystem. 45034461Speter So we first call stat() to see if it is OK to call unlink(). This 45117721Speter doesn't quite work--if someone creates a directory between the 45234461Speter call to stat() and the call to unlink(), we'll still corrupt 45317721Speter the filesystem. Where is the Unix Haters Handbook when you need 45417721Speter it? */ 45534461Speter if (stat (f, &sb) < 0) 45617721Speter { 45734461Speter if (existence_error (errno)) 45834461Speter { 45934461Speter /* The file or directory doesn't exist anyhow. */ 46017721Speter return -1; 46134461Speter } 46217721Speter } 46334461Speter else if (S_ISDIR (sb.st_mode)) 46434461Speter return deep_remove_dir (f); 46534461Speter 46681407Speter return CVS_UNLINK (f); 46717721Speter} 46817721Speter 46917721Speter/* Remove a directory and everything it contains. Returns 0 for 47017721Speter * success, -1 for failure (in which case errno is set). 47117721Speter */ 47217721Speter 47317721Speterstatic int 47417721Speterdeep_remove_dir (path) 47517721Speter const char *path; 47617721Speter{ 47717721Speter DIR *dirp; 47817721Speter struct dirent *dp; 47917721Speter 48025839Speter if (rmdir (path) != 0) 48117721Speter { 48225839Speter if (errno == ENOTEMPTY 48325839Speter || errno == EEXIST 48425839Speter /* Ugly workaround for ugly AIX 4.1 (and 3.2) header bug 48525839Speter (it defines ENOTEMPTY and EEXIST to 17 but actually 48625839Speter returns 87). */ 48725839Speter || (ENOTEMPTY == 17 && EEXIST == 17 && errno == 87)) 48817721Speter { 48981407Speter if ((dirp = CVS_OPENDIR (path)) == NULL) 49025839Speter /* If unable to open the directory return 49125839Speter * an error 49225839Speter */ 49325839Speter return -1; 49417721Speter 49554431Speter errno = 0; 49681407Speter while ((dp = CVS_READDIR (dirp)) != NULL) 49725839Speter { 49825839Speter char *buf; 49917721Speter 50025839Speter if (strcmp (dp->d_name, ".") == 0 || 50125839Speter strcmp (dp->d_name, "..") == 0) 50225839Speter continue; 50325839Speter 50425839Speter buf = xmalloc (strlen (path) + strlen (dp->d_name) + 5); 50525839Speter sprintf (buf, "%s/%s", path, dp->d_name); 50625839Speter 50725839Speter /* See comment in unlink_file_dir explanation of why we use 50825839Speter isdir instead of just calling unlink and checking the 50925839Speter status. */ 51025839Speter if (isdir(buf)) 51117721Speter { 51225839Speter if (deep_remove_dir(buf)) 51325839Speter { 51481407Speter CVS_CLOSEDIR(dirp); 51525839Speter free (buf); 51625839Speter return -1; 51725839Speter } 51817721Speter } 51925839Speter else 52017721Speter { 52181407Speter if (CVS_UNLINK (buf) != 0) 52225839Speter { 52381407Speter CVS_CLOSEDIR(dirp); 52425839Speter free (buf); 52525839Speter return -1; 52625839Speter } 52717721Speter } 52825839Speter free (buf); 52954431Speter 53054431Speter errno = 0; 53117721Speter } 53254431Speter if (errno != 0) 53354431Speter { 53454431Speter int save_errno = errno; 53581407Speter CVS_CLOSEDIR (dirp); 53654431Speter errno = save_errno; 53754431Speter return -1; 53854431Speter } 53981407Speter CVS_CLOSEDIR (dirp); 54025839Speter return rmdir (path); 54117721Speter } 54225839Speter else 54325839Speter return -1; 54425839Speter } 54517721Speter 54617721Speter /* Was able to remove the directory return 0 */ 54717721Speter return 0; 54817721Speter} 54917721Speter 55017721Speter/* Read NCHARS bytes from descriptor FD into BUF. 55117721Speter Return the number of characters successfully read. 55217721Speter The number returned is always NCHARS unless end-of-file or error. */ 55317721Speterstatic size_t 55417721Speterblock_read (fd, buf, nchars) 55517721Speter int fd; 55617721Speter char *buf; 55717721Speter size_t nchars; 55817721Speter{ 55917721Speter char *bp = buf; 56017721Speter size_t nread; 56117721Speter 56217721Speter do 56317721Speter { 56417721Speter nread = read (fd, bp, nchars); 56517721Speter if (nread == (size_t)-1) 56617721Speter { 56717721Speter#ifdef EINTR 56817721Speter if (errno == EINTR) 56917721Speter continue; 57017721Speter#endif 57117721Speter return (size_t)-1; 57217721Speter } 57317721Speter 57417721Speter if (nread == 0) 57517721Speter break; 57617721Speter 57717721Speter bp += nread; 57817721Speter nchars -= nread; 57917721Speter } while (nchars != 0); 58017721Speter 58117721Speter return bp - buf; 58217721Speter} 58317721Speter 58417721Speter 58517721Speter/* 58617721Speter * Compare "file1" to "file2". Return non-zero if they don't compare exactly. 58734461Speter * If FILE1 and FILE2 are special files, compare their salient characteristics 58834461Speter * (i.e. major/minor device numbers, links, etc. 58917721Speter */ 59017721Speterint 59117721Speterxcmp (file1, file2) 59217721Speter const char *file1; 59317721Speter const char *file2; 59417721Speter{ 59517721Speter char *buf1, *buf2; 59617721Speter struct stat sb1, sb2; 59717721Speter int fd1, fd2; 59817721Speter int ret; 59917721Speter 60034461Speter if (CVS_LSTAT (file1, &sb1) < 0) 60134461Speter error (1, errno, "cannot lstat %s", file1); 60234461Speter if (CVS_LSTAT (file2, &sb2) < 0) 60334461Speter error (1, errno, "cannot lstat %s", file2); 60434461Speter 60534461Speter /* If FILE1 and FILE2 are not the same file type, they are unequal. */ 60634461Speter if ((sb1.st_mode & S_IFMT) != (sb2.st_mode & S_IFMT)) 60734461Speter return 1; 60834461Speter 60934461Speter /* If FILE1 and FILE2 are symlinks, they are equal if they point to 61034461Speter the same thing. */ 611128269Speter#ifdef S_ISLNK 61234461Speter if (S_ISLNK (sb1.st_mode) && S_ISLNK (sb2.st_mode)) 61334461Speter { 61434461Speter int result; 61534461Speter buf1 = xreadlink (file1); 61634461Speter buf2 = xreadlink (file2); 61734461Speter result = (strcmp (buf1, buf2) == 0); 61834461Speter free (buf1); 61934461Speter free (buf2); 62034461Speter return result; 62134461Speter } 622128269Speter#endif 62334461Speter 62434461Speter /* If FILE1 and FILE2 are devices, they are equal if their device 62534461Speter numbers match. */ 62634461Speter if (S_ISBLK (sb1.st_mode) || S_ISCHR (sb1.st_mode)) 62734461Speter { 628107487Speter#ifdef HAVE_STRUCT_STAT_ST_RDEV 62934461Speter if (sb1.st_rdev == sb2.st_rdev) 63034461Speter return 0; 63134461Speter else 63234461Speter return 1; 63366528Speter#else 63466528Speter error (1, 0, "cannot compare device files on this system (%s and %s)", 63566528Speter file1, file2); 63666528Speter#endif 63734461Speter } 63834461Speter 63917721Speter if ((fd1 = open (file1, O_RDONLY)) < 0) 64017721Speter error (1, errno, "cannot open file %s for comparing", file1); 64117721Speter if ((fd2 = open (file2, O_RDONLY)) < 0) 64217721Speter error (1, errno, "cannot open file %s for comparing", file2); 64317721Speter 64417721Speter /* A generic file compare routine might compare st_dev & st_ino here 64517721Speter to see if the two files being compared are actually the same file. 64617721Speter But that won't happen in CVS, so we won't bother. */ 64717721Speter 64817721Speter if (sb1.st_size != sb2.st_size) 64917721Speter ret = 1; 65017721Speter else if (sb1.st_size == 0) 65117721Speter ret = 0; 65217721Speter else 65317721Speter { 65417721Speter /* FIXME: compute the optimal buffer size by computing the least 65517721Speter common multiple of the files st_blocks field */ 65617721Speter size_t buf_size = 8 * 1024; 65717721Speter size_t read1; 65817721Speter size_t read2; 65917721Speter 66017721Speter buf1 = xmalloc (buf_size); 66117721Speter buf2 = xmalloc (buf_size); 66217721Speter 66317721Speter do 66417721Speter { 66517721Speter read1 = block_read (fd1, buf1, buf_size); 66617721Speter if (read1 == (size_t)-1) 66717721Speter error (1, errno, "cannot read file %s for comparing", file1); 66817721Speter 66917721Speter read2 = block_read (fd2, buf2, buf_size); 67017721Speter if (read2 == (size_t)-1) 67117721Speter error (1, errno, "cannot read file %s for comparing", file2); 67217721Speter 67317721Speter /* assert (read1 == read2); */ 67417721Speter 67517721Speter ret = memcmp(buf1, buf2, read1); 67617721Speter } while (ret == 0 && read1 == buf_size); 67717721Speter 67817721Speter free (buf1); 67917721Speter free (buf2); 68017721Speter } 68117721Speter 68217721Speter (void) close (fd1); 68317721Speter (void) close (fd2); 68417721Speter return (ret); 68517721Speter} 68625839Speter 68725839Speter/* Generate a unique temporary filename. Returns a pointer to a newly 68881407Speter * malloc'd string containing the name. Returns successfully or not at 68981407Speter * all. 69081407Speter * 69181407Speter * THIS FUNCTION IS DEPRECATED!!! USE cvs_temp_file INSTEAD!!! 69281407Speter * 69381407Speter * and yes, I know about the way the rcs commands use temp files. I think 69481407Speter * they should be converted too but I don't have time to look into it right 69581407Speter * now. 69681407Speter */ 69754440Speterchar * 69854440Spetercvs_temp_name () 69954440Speter{ 70081407Speter char *fn; 70181407Speter FILE *fp; 70254440Speter 70381407Speter fp = cvs_temp_file (&fn); 70481407Speter if (fp == NULL) 705175269Sobrien error (1, errno, "Failed to create temporary file %s", 706175269Sobrien fn ? fn : "(null)"); 70781407Speter if (fclose (fp) == EOF) 70881407Speter error (0, errno, "Failed to close temporary file %s", fn); 70981407Speter return fn; 71081407Speter} 71154440Speter 71281407Speter/* Generate a unique temporary filename and return an open file stream 71381407Speter * to the truncated file by that name 71481407Speter * 71581407Speter * INPUTS 71681407Speter * filename where to place the pointer to the newly allocated file 71781407Speter * name string 71881407Speter * 71981407Speter * OUTPUTS 72081407Speter * filename dereferenced, will point to the newly allocated file 72181407Speter * name string. This value is undefined if the function 72281407Speter * returns an error. 72381407Speter * 72481407Speter * RETURNS 72581407Speter * An open file pointer to a read/write mode empty temporary file with the 72681407Speter * unique file name or NULL on failure. 72781407Speter * 72881407Speter * ERRORS 72981407Speter * on error, errno will be set to some value either by CVS_FOPEN or 73081407Speter * whatever system function is called to generate the temporary file name 73181407Speter */ 73281407Speter/* There are at least four functions for generating temporary 73381407Speter * filenames. We use mkstemp (BSD 4.3) if possible, else tempnam (SVID 3), 73481407Speter * else mktemp (BSD 4.3), and as last resort tmpnam (POSIX). Reason is that 73581407Speter * mkstemp, tempnam, and mktemp both allow to specify the directory in which 73681407Speter * the temporary file will be created. 73781407Speter * 73881407Speter * And the _correct_ way to use the deprecated functions probably involves 73981407Speter * opening file descriptors using O_EXCL & O_CREAT and even doing the annoying 74081407Speter * NFS locking thing, but until I hear of more problems, I'm not going to 74181407Speter * bother. 74281407Speter */ 743175269SobrienFILE * 744175269Sobriencvs_temp_file (filename) 74581407Speter char **filename; 74625839Speter{ 74781407Speter char *fn; 74881407Speter FILE *fp; 74917721Speter 75081407Speter /* FIXME - I'd like to be returning NULL here in noexec mode, but I think 75181407Speter * some of the rcs & diff functions which rely on a temp file run in 75281407Speter * noexec mode too. 75381407Speter */ 75481407Speter 75581407Speter assert (filename != NULL); 75681407Speter 75781407Speter#ifdef HAVE_MKSTEMP 75881407Speter 75981407Speter { 76081407Speter int fd; 76181407Speter 76281407Speter fn = xmalloc (strlen (Tmpdir) + 11); 76381407Speter sprintf (fn, "%s/%s", Tmpdir, "cvsXXXXXX" ); 76481407Speter fd = mkstemp (fn); 76581407Speter 76681407Speter /* a NULL return will be interpreted by callers as an error and 76781407Speter * errno should still be set 76881407Speter */ 76981407Speter if (fd == -1) fp = NULL; 77081407Speter else if ((fp = CVS_FDOPEN (fd, "w+")) == NULL) 77181407Speter { 772128269Speter /* Attempt to close and unlink the file since mkstemp returned 773128269Speter * sucessfully and we believe it's been created and opened. 77481407Speter */ 77581407Speter int save_errno = errno; 77681407Speter if (close (fd)) 77781407Speter error (0, errno, "Failed to close temporary file %s", fn); 77881407Speter if (CVS_UNLINK (fn)) 77981407Speter error (0, errno, "Failed to unlink temporary file %s", fn); 78081407Speter errno = save_errno; 78181407Speter } 78281407Speter 783175269Sobrien if (fp == NULL) 784175269Sobrien { 785175269Sobrien free (fn); 786175269Sobrien fn = NULL; 787175269Sobrien } 78881407Speter /* mkstemp is defined to open mode 0600 using glibc 2.0.7+ */ 78981407Speter /* FIXME - configure can probably tell us which version of glibc we are 79081407Speter * linking to and not chmod for 2.0.7+ 79181407Speter */ 79281407Speter else chmod (fn, 0600); 79381407Speter 79481407Speter } 79581407Speter 79681407Speter#elif HAVE_TEMPNAM 79781407Speter 79881407Speter /* tempnam has been deprecated due to under-specification */ 79981407Speter 80081407Speter fn = tempnam (Tmpdir, "cvs"); 80181407Speter if (fn == NULL) fp = NULL; 802175269Sobrien else if ((fp = CVS_FOPEN (fn, "w+")) == NULL) 803175269Sobrien { 804175269Sobrien free (fn); 805175269Sobrien fn = NULL; 806175269Sobrien } 80781407Speter else chmod (fn, 0600); 80881407Speter 80925839Speter /* tempnam returns a pointer to a newly malloc'd string, so there's 81081407Speter * no need for a xstrdup 81181407Speter */ 81217721Speter 81381407Speter#elif HAVE_MKTEMP 81425839Speter 81581407Speter /* mktemp has been deprecated due to the BSD 4.3 specification specifying 81681407Speter * that XXXXXX will be replaced by a PID and a letter, creating only 26 81781407Speter * possibilities, a security risk, and a race condition. 81881407Speter */ 81925839Speter 82081407Speter { 82181407Speter char *ifn; 82281407Speter 82381407Speter ifn = xmalloc (strlen (Tmpdir) + 11); 82481407Speter sprintf (ifn, "%s/%s", Tmpdir, "cvsXXXXXX" ); 82581407Speter fn = mktemp (ifn); 82681407Speter 82781407Speter if (fn == NULL) fp = NULL; 82881407Speter else fp = CVS_FOPEN (fn, "w+"); 82981407Speter 83081407Speter if (fp == NULL) free (ifn); 83181407Speter else chmod (fn, 0600); 83281407Speter 83381407Speter } 83481407Speter 83581407Speter#else /* use tmpnam if all else fails */ 83681407Speter 83781407Speter /* tmpnam is deprecated */ 83881407Speter 83981407Speter { 84081407Speter char ifn[L_tmpnam + 1]; 84181407Speter 84281407Speter fn = tmpnam (ifn); 84381407Speter 84481407Speter if (fn == NULL) fp = NULL; 84581407Speter else if ((fp = CVS_FOPEN (ifn, "w+")) != NULL) 84681407Speter { 84781407Speter fn = xstrdup (ifn); 84881407Speter chmod (fn, 0600); 84981407Speter } 85081407Speter 85181407Speter } 85281407Speter 85381407Speter#endif 85481407Speter 85581407Speter *filename = fn; 856175269Sobrien if (fn == NULL && fp != NULL) 857175269Sobrien { 858175269Sobrien fclose (fp); 859175269Sobrien fp = NULL; 860175269Sobrien } 86181407Speter return fp; 86217721Speter} 86381407Speter 86417721Speter 865128269Speter 866128269Speter#ifdef HAVE_READLINK 867128269Speter/* char * 868128269Speter * xreadlink ( const char *link ) 869128269Speter * 870128269Speter * Like the X/OPEN and 4.4BSD readlink() function, but allocates and returns 871128269Speter * its own buf. 872128269Speter * 873128269Speter * INPUTS 874128269Speter * link The original path. 875128269Speter * 876128269Speter * RETURNS 877128269Speter * The resolution of the final symbolic link in the path. 878128269Speter * 879128269Speter * ERRORS 880128269Speter * This function exits with a fatal error if it fails to read the link for 881128269Speter * any reason. 88234461Speter */ 883175269Sobrien#define MAXSIZE (SIZE_MAX < SSIZE_MAX ? SIZE_MAX : SSIZE_MAX) 884175269Sobrien 88534461Speterchar * 88634461Speterxreadlink (link) 88734461Speter const char *link; 88834461Speter{ 88934461Speter char *file = NULL; 890175269Sobrien size_t buflen = BUFSIZ; 89117721Speter 892175269Sobrien /* Get the name of the file to which `from' is linked. */ 893175269Sobrien while (1) 89434461Speter { 895175269Sobrien ssize_t r; 896175269Sobrien size_t link_name_len; 897175269Sobrien 89834461Speter file = xrealloc (file, buflen); 899175269Sobrien r = readlink (link, file, buflen); 900175269Sobrien link_name_len = r; 90134461Speter 902175269Sobrien if (r < 0 903175269Sobrien#ifdef ERANGE 904175269Sobrien /* AIX 4 and HP-UX report ERANGE if the buffer is too small. */ 905175269Sobrien && errno != ERANGE 906175269Sobrien#endif 907175269Sobrien ) 908175269Sobrien error (1, errno, "cannot readlink %s", link); 90934461Speter 910175269Sobrien /* If there is space for the NUL byte, set it and return. */ 911175269Sobrien if (r >= 0 && link_name_len < buflen) 912175269Sobrien { 913175269Sobrien file[link_name_len] = '\0'; 914175269Sobrien return file; 915175269Sobrien } 916175008Sobrien 917175269Sobrien if (buflen <= MAXSIZE / 2) 918175269Sobrien buflen *= 2; 919175269Sobrien else if (buflen < MAXSIZE) 920175269Sobrien buflen = MAXSIZE; 921175269Sobrien else 922175269Sobrien /* Our buffer cannot grow any bigger. */ 923175269Sobrien error (1, ENAMETOOLONG, "cannot readlink %s", link); 924175269Sobrien } 925128269Speter} 926128269Speter#endif /* HAVE_READLINK */ 92744856Speter 928128269Speter 929128269Speter 930128269Speter/* char * 931128269Speter * xresolvepath ( const char *path ) 932128269Speter * 933128269Speter * Like xreadlink(), but resolve all links in a path. 934128269Speter * 935128269Speter * INPUTS 936128269Speter * path The original path. 937128269Speter * 938128269Speter * RETURNS 939128269Speter * The path with any symbolic links expanded. 940128269Speter * 941128269Speter * ERRORS 942128269Speter * This function exits with a fatal error if it fails to read the link for 943128269Speter * any reason. 944128269Speter */ 945128269Speterchar * 946128269Speterxresolvepath ( path ) 947128269Speter const char *path; 948128269Speter{ 949128269Speter char *hardpath; 950128269Speter char *owd; 951128269Speter 952128269Speter assert ( isdir ( path ) ); 953128269Speter 954128269Speter /* FIXME - If HAVE_READLINK is defined, we should probably walk the path 955128269Speter * bit by bit calling xreadlink(). 956128269Speter */ 957128269Speter 958128269Speter owd = xgetwd(); 959128269Speter if ( CVS_CHDIR ( path ) < 0) 960128269Speter error ( 1, errno, "cannot chdir to %s", path ); 961128269Speter if ( ( hardpath = xgetwd() ) == NULL ) 962128269Speter error (1, errno, "cannot getwd in %s", path); 963128269Speter if ( CVS_CHDIR ( owd ) < 0) 964128269Speter error ( 1, errno, "cannot chdir to %s", owd ); 965128269Speter free (owd); 966128269Speter return hardpath; 96734461Speter} 96834461Speter 96934461Speter 970128269Speter 97117721Speter/* Return a pointer into PATH's last component. */ 972128269Speterconst char * 97317721Speterlast_component (path) 974128269Speter const char *path; 97517721Speter{ 976128269Speter const char *last = strrchr (path, '/'); 977175269Sobrien 978175269Sobrien assert (path); 97934461Speter if (last && (last != path)) 98017721Speter return last + 1; 98117721Speter else 98217721Speter return path; 98317721Speter} 98417721Speter 98517721Speter/* Return the home directory. Returns a pointer to storage 98625839Speter managed by this function or its callees (currently getenv). 98725839Speter This function will return the same thing every time it is 98832785Speter called. Returns NULL if there is no home directory. 98932785Speter 99032785Speter Note that for a pserver server, this may return root's home 99132785Speter directory. What typically happens is that upon being started from 99232785Speter inetd, before switching users, the code in cvsrc.c calls 99332785Speter get_homedir which remembers root's home directory in the static 99432785Speter variable. Then the switch happens and get_homedir might return a 99532785Speter directory that we don't even have read or execute permissions for 99632785Speter (which is bad, when various parts of CVS try to read there). One 99732785Speter fix would be to make the value returned by get_homedir only good 99832785Speter until the next call (which would free the old value). Another fix 99932785Speter would be to just always malloc our answer, and let the caller free 100032785Speter it (that is best, because some day we may need to be reentrant). 100132785Speter 100232785Speter The workaround is to put -f in inetd.conf which means that 100332785Speter get_homedir won't get called until after the switch in user ID. 100432785Speter 100532785Speter The whole concept of a "home directory" on the server is pretty 100632785Speter iffy, although I suppose some people probably are relying on it for 100732785Speter .cvsrc and such, in the cases where it works. */ 100817721Speterchar * 100917721Speterget_homedir () 101017721Speter{ 101125839Speter static char *home = NULL; 101281407Speter char *env; 101325839Speter struct passwd *pw; 101425839Speter 101525839Speter if (home != NULL) 101625839Speter return home; 101725839Speter 1018175269Sobrien if (!server_active && (env = getenv ("HOME")) != NULL) 101925839Speter home = env; 102025839Speter else if ((pw = (struct passwd *) getpwuid (getuid ())) 102125839Speter && pw->pw_dir) 102225839Speter home = xstrdup (pw->pw_dir); 102325839Speter else 102425839Speter return 0; 102525839Speter 102625839Speter return home; 102717721Speter} 102817721Speter 1029107487Speter/* Compose a path to a file in the home directory. This is necessary because 1030107487Speter * of different behavior on UNIX and VMS. See the notes in vms/filesubr.c. 1031107487Speter * 1032107487Speter * A more clean solution would be something more along the lines of a 1033107487Speter * "join a directory to a filename" kind of thing which was not specific to 1034107487Speter * the homedir. This should aid portability between UNIX, Mac, Windows, VMS, 1035107487Speter * and possibly others. This is already handled by Perl - it might be 1036107487Speter * interesting to see how much of the code was written in C since Perl is under 1037107487Speter * the GPL and the Artistic license - we might be able to use it. 1038107487Speter */ 1039107487Speterchar * 1040107487Speterstrcat_filename_onto_homedir (dir, file) 1041107487Speter const char *dir; 1042107487Speter const char *file; 1043107487Speter{ 1044107487Speter char *path = xmalloc (strlen (dir) + 1 + strlen(file) + 1); 1045107487Speter sprintf (path, "%s/%s", dir, file); 1046107487Speter return path; 1047107487Speter} 1048107487Speter 104917721Speter/* See cvs.h for description. On unix this does nothing, because the 105017721Speter shell expands the wildcards. */ 105117721Spetervoid 105217721Speterexpand_wild (argc, argv, pargc, pargv) 105317721Speter int argc; 105417721Speter char **argv; 105517721Speter int *pargc; 105617721Speter char ***pargv; 105717721Speter{ 105817721Speter int i; 1059175269Sobrien assert (argv || !argc); 1060130307Speter if (size_overflow_p (xtimes (argc, sizeof (char *)))) { 1061130307Speter *pargc = 0; 1062130307Speter *pargv = NULL; 1063130307Speter error (0, 0, "expand_wild: too many arguments"); 1064130307Speter return; 1065130307Speter } 106617721Speter *pargc = argc; 1067130307Speter *pargv = xmalloc (xtimes (argc, sizeof (char *))); 106817721Speter for (i = 0; i < argc; ++i) 106917721Speter (*pargv)[i] = xstrdup (argv[i]); 107017721Speter} 107125839Speter 1072128269Speter 1073128269Speter 107425839Speter#ifdef SERVER_SUPPORT 107525839Speter/* Case-insensitive string compare. I know that some systems 107625839Speter have such a routine, but I'm not sure I see any reasons for 107725839Speter dealing with the hair of figuring out whether they do (I haven't 107825839Speter looked into whether this is a performance bottleneck; I would guess 107925839Speter not). */ 108025839Speterint 108125839Spetercvs_casecmp (str1, str2) 1082128269Speter const char *str1; 1083128269Speter const char *str2; 108425839Speter{ 1085128269Speter const char *p; 1086128269Speter const char *q; 108725839Speter int pqdiff; 108825839Speter 108925839Speter p = str1; 109025839Speter q = str2; 109125839Speter while ((pqdiff = tolower (*p) - tolower (*q)) == 0) 109225839Speter { 109325839Speter if (*p == '\0') 109425839Speter return 0; 109525839Speter ++p; 109625839Speter ++q; 109725839Speter } 109825839Speter return pqdiff; 109925839Speter} 110025839Speter#endif /* SERVER_SUPPORT */ 1101