[gnu.emacs.bug] making movemail more flexible

djm@ABYSS.ENG.UMD.EDU (David J. MacKenzie) (12/02/89)

On our Sun NFS network here, /usr/spool/mail/<user> is a symbolic link
to /mail/<server>/<user>, where <server> is one of several machines
that depends on how the user got the account.  All mail is delivered
(by /usr/bin/mail) on the servers.  The /usr/spool directories are
actually symbolic links to /var/spool, and are local to each
workstation.  This setup allows us to have the various /mail/<server>
directories automounted with NFS only when they are needed.

It is my impression that this is a fairly common arrangement for Sun NFS
networks.

It has two implications for movemail:

1.  Locking /usr/spool/mail/<user> is useless, because it locks the
symbolic link in the workstation's /var/spool rather than the actual
mail spool file on the server.

2.  /usr/spool/mail/<user> should not be deleted, because if it were,
the link to /mail/<server>/<user> would be lost.  Instead, the file
that it points to should be deleted.

Below are some additions to etc/movemail.c that
1) automatically trace any number of levels of symbolic links
encountered when trying to lstat /usr/spool/mail/<user>, to arrive at
the mail file's real pathname (so that when ".lock" can be appended to
it, the lock will appear in the server's mail directory)

2) add a compile-time option to never try to unlink the mail spool file,
but always truncate it.  Some sites have permissions set up so that this
is necessary.  If it is removed (this option is not defined), the file
that will be removed is the one on the server, not the local symbolic
link.


*** movemail.c	Mon Nov 27 12:57:30 1989
--- movemail-local.c	Wed Nov 29 10:20:01 1989
***************
*** 70,75 ****
--- 70,179 ----
  /* Nonzero means this is name of a lock file to delete on fatal error.  */
  char *delete_lockname;
  
+ #ifdef S_IFLNK
+ /* The amount of memory to increase the size of the symbolic link name
+    buffer by until the name fits. */
+ #define BLOCK 100
+ 
+ char *malloc ();
+ char *strrchr ();
+ 
+ static char *
+ basename (str)
+      char *str;
+ {
+   char *base;
+   
+   base = strrchr (str, '/');
+   if (base)
+     return base + 1;
+   else
+     return 0;
+ }
+ 
+ static char *
+ follow_link (path)
+      char *path;
+ {
+   char *linkval, *newlink, *pathend;
+   size_t linklen;
+   int ret;
+ 
+   linklen = BLOCK;
+   linkval = malloc (linklen);
+   if (linkval == 0)
+     return 0;
+   while ((ret = readlink (path, linkval, linklen)) != -1 && ret == linklen)
+     {
+       free (linkval);
+       linklen += BLOCK;
+       linkval = malloc (linklen);
+       if (linkval == 0)
+ 	return 0;
+     }
+ 
+   if (ret == -1)
+     return 0;
+   linkval[ret] = 0;		/* readlink does not null terminate. */
+ 
+   /* Correct for a relative link. */
+   if (*linkval != '/')
+     {
+       pathend = basename (path);
+       newlink = malloc (ret + strlen (path) + 1);
+       if (newlink == 0)
+ 	return 0;
+       strncpy (newlink, path, pathend - path);
+       if (newlink [strlen (newlink) - 1] != '/')
+ 	strcat (newlink, "/");
+       strcat (newlink, linkval);
+       return newlink;
+     }
+   else
+     return linkval;
+ }
+ 
+ static int
+ filetype (path)
+      char *path;
+ {
+   struct stat stats;
+ 
+   if (lstat (path, &stats) == -1)
+     return 0;
+   else
+     return stats.st_mode & S_IFMT;
+ }
+ 
+ /* Follow any number of levels of symbolic links to return a non-symlink
+    file that is referenced by `path'.  If `path' does not refer to a symlink,
+    return `path'.  If there is an error, return 0. */
+ 
+ char *
+ realpath(path)
+      char *path;
+ {
+   char *newpath;
+   int len;
+ 
+   if (path == 0)
+     return 0;
+ 
+   /* Remove trailing slashes. */
+   len = strlen (path) - 1;
+   while (len > 0 && path[len] == '/')
+       path[len--] = 0;
+ 
+   while (path && filetype (path) == S_IFLNK)
+     {
+       newpath = follow_link (path);
+       free (path);
+       path = newpath;
+     }
+   return path;
+ }
+ #endif /* S_IFLNK */
+ 
  main (argc, argv)
       int argc;
       char **argv;
***************
*** 93,99 ****
--- 197,207 ----
    if (argc < 3)
      fatal ("two arguments required");
  
+ #ifdef S_IFLNK
+   inname = realpath (argv[1]);
+ #else
    inname = argv[1];
+ #endif /* S_IFLNK */
    outname = argv[2];
  
    /* Check access to input and output file.  */
***************
*** 235,242 ****
--- 343,352 ----
  
  #ifndef MAIL_USE_FLOCK
    /* Delete the input file; if we can't, at least get rid of its contents.  */
+ #ifndef SAVE_INBOX
    if (unlink (inname) < 0)
      if (errno != ENOENT)
+ #endif /* SAVE_INBOX */
        creat (inname, 0666);
    (void) unlink (lockname);
  #endif /* not MAIL_USE_FLOCK */
***************