Subject: bin/4239: Quota support for mail.local
To: None <gnats-bugs@gnats.netbsd.org>
From: None <jmarin@jmp.fi>
List: netbsd-bugs
Date: 10/08/1997 11:50:31
>Number:         4239
>Category:       bin
>Synopsis:       Adding user quota support to mail.local
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    bin-bug-people (Utility Bug People)
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Wed Oct  8 02:05:01 1997
>Last-Modified:
>Originator:     Jukka Marin
>Organization:
JMP-Electronics
>Release:        1.2
>Environment:
	NetBSD 1.2 and -current

>Description:
	mail.local doesn't check the user quota, so mail boxes can grow
	and fill the entire disk partition.  The supplied fix adds a
	new command line option to mail.local to enable quota checking.
>How-To-Repeat:
	Send huge mail messages -> the spool partition fills up.
>Fix:
	Apply this patch to mail.local of NetBSD 1.2:

*** mail.local.c.orig	Wed Oct  8 11:40:30 1997
--- mail.local.c	Wed Oct  8 11:40:47 1997
***************
*** 53,58 ****
--- 53,59 ----
  #include <time.h>
  #include <unistd.h>
  #include <errno.h>
+ #include <sysexits.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
***************
*** 61,67 ****
  #define	FATAL		1
  #define	NOTFATAL	0
  
! int	deliver __P((int, char *, int));
  void	err __P((int, const char *, ...));
  void	notifybiff __P((char *));
  int	store __P((char *));
--- 62,68 ----
  #define	FATAL		1
  #define	NOTFATAL	0
  
! int	deliver __P((int, char *, int, int));
  void	err __P((int, const char *, ...));
  void	notifybiff __P((char *));
  int	store __P((char *));
***************
*** 74,87 ****
  	extern int optind;
  	extern char *optarg;
  	struct passwd *pw;
! 	int ch, fd, eval, lockfile=0;
  	uid_t uid;
  	char *from;
  
  	openlog("mail.local", LOG_PERROR, LOG_MAIL);
  
  	from = NULL;
! 	while ((ch = getopt(argc, argv, "ldf:r:")) != EOF)
  		switch(ch) {
  		case 'd':		/* backward compatible */
  			break;
--- 75,88 ----
  	extern int optind;
  	extern char *optarg;
  	struct passwd *pw;
! 	int ch, fd, eval, lockfile=0, quota=0;
  	uid_t uid;
  	char *from;
  
  	openlog("mail.local", LOG_PERROR, LOG_MAIL);
  
  	from = NULL;
! 	while ((ch = getopt(argc, argv, "ldf:qr:")) != EOF)
  		switch(ch) {
  		case 'd':		/* backward compatible */
  			break;
***************
*** 94,99 ****
--- 95,103 ----
  		case 'l':
  			lockfile++;
  			break;
+ 		case 'q':
+ 			quota++;
+ 			break;
  		case '?':
  		default:
  			usage();
***************
*** 116,122 ****
  
  	fd = store(from);
  	for (eval = 0; *argv; ++argv)
! 		eval |= deliver(fd, *argv, lockfile);
  	exit(eval);
  }
  
--- 120,126 ----
  
  	fd = store(from);
  	for (eval = 0; *argv; ++argv)
! 		eval |= deliver(fd, *argv, lockfile, quota);
  	exit(eval);
  }
  
***************
*** 163,169 ****
  	return(fd);
  }
  
! deliver(fd, name, lockfile)
  	int fd;
  	char *name;
  	int lockfile;
--- 167,173 ----
  	return(fd);
  }
  
! deliver(fd, name, lockfile, quota)
  	int fd;
  	char *name;
  	int lockfile;
***************
*** 173,178 ****
--- 177,183 ----
  	int created, mbfd, nr, nw, off, rval=0, lfd=-1;
  	char biffmsg[100], buf[8*1024], path[MAXPATHLEN], lpath[MAXPATHLEN];
  	off_t curoff;
+ 	uid_t oeuid;
  
  	/*
  	 * Disallow delivery to unknown names -- special mailboxes can be
***************
*** 200,213 ****
  		err(NOTFATAL, "%s: linked file", path);
  		return(1);
  	}
! 	if((mbfd = open(path, O_APPEND|O_WRONLY|O_EXLOCK,
! 	    S_IRUSR|S_IWUSR)) < 0) {
  		if ((mbfd = open(path, O_APPEND|O_CREAT|O_WRONLY|O_EXLOCK,
  		    S_IRUSR|S_IWUSR)) < 0) {
  		err(NOTFATAL, "%s: %s", path, strerror(errno));
  		return(1);
  	}
- 	}
  
  	curoff = lseek(mbfd, 0, SEEK_END);
  	(void)sprintf(biffmsg, "%s@%qd\n", name, curoff);
--- 205,235 ----
  		err(NOTFATAL, "%s: linked file", path);
  		return(1);
  	}
! 
! 	/* if mailbox does not exist, create it */
! 	if (created) {
  		if ((mbfd = open(path, O_APPEND|O_CREAT|O_WRONLY|O_EXLOCK,
  		    S_IRUSR|S_IWUSR)) < 0) {
+ 			err(NOTFATAL, "%s: %s", path, strerror(errno));
+ 			return(1);
+ 		}
+ 		(void)fchown(mbfd, pw->pw_uid, pw->pw_gid);
+ 		(void)close(mbfd);
+ 	}
+ 
+ 	/* save original effective uid */
+ 	oeuid = geteuid();
+ 
+ 	/* if told to look for quota limits, use user privileges */
+ 	if (quota)
+ 		seteuid(pw->pw_uid);
+ 
+ 	if((mbfd = open(path, O_APPEND|O_WRONLY|O_EXLOCK,
+ 	    S_IRUSR|S_IWUSR)) < 0) {
  		err(NOTFATAL, "%s: %s", path, strerror(errno));
+ 		/* return original euid */
  		return(1);
  	}
  
  	curoff = lseek(mbfd, 0, SEEK_END);
  	(void)sprintf(biffmsg, "%s@%qd\n", name, curoff);
***************
*** 220,249 ****
  	while ((nr = read(fd, buf, sizeof(buf))) > 0)
  		for (off = 0; off < nr;  off += nw)
  			if ((nw = write(mbfd, buf + off, nr - off)) < 0) {
  				err(NOTFATAL, "%s: %s", path, strerror(errno));
! 				goto trunc;
  			}
  	if (nr < 0) {
  		err(FATAL, "temporary file: %s", strerror(errno));
- trunc:		(void)ftruncate(mbfd, curoff);
  		rval = 1;
  	}
  
- 	/*
- 	 * Set the owner and group.  Historically, binmail repeated this at
- 	 * each mail delivery.  We no longer do this, assuming that if the
- 	 * ownership or permissions were changed there was a reason for doing
- 	 * so.
- 	 */
  bad:
  	if(lockfile) {
  		if(lfd >= 0) {
  			unlink(lpath);
  			close(lfd);
  		}
  	}
- 	if (created) 
- 		(void)fchown(mbfd, pw->pw_uid, pw->pw_gid);
  
  	(void)fsync(mbfd);		/* Don't wait for update. */
  	(void)close(mbfd);		/* Implicit unlock. */
--- 242,273 ----
  	while ((nr = read(fd, buf, sizeof(buf))) > 0)
  		for (off = 0; off < nr;  off += nw)
  			if ((nw = write(mbfd, buf + off, nr - off)) < 0) {
+ 			    if (errno == EDQUOT) {
+ 				err(NOTFATAL, "User %s mailbox is full (%s)",
+ 					pw->pw_name, strerror(errno));
+ 				rval = 1 /*EX_TEMPFAIL*/;
+ 			    } else {
  				err(NOTFATAL, "%s: %s", path, strerror(errno));
! 				rval = 1;
! 			    }
! 			    goto trunc;
  			}
  	if (nr < 0) {
  		err(FATAL, "temporary file: %s", strerror(errno));
  		rval = 1;
+ trunc:		(void)ftruncate(mbfd, curoff);
  	}
  
  bad:
+ 	/* return original euid */
+ 	seteuid(oeuid);
+ 
  	if(lockfile) {
  		if(lfd >= 0) {
  			unlink(lpath);
  			close(lfd);
  		}
  	}
  
  	(void)fsync(mbfd);		/* Don't wait for update. */
  	(void)close(mbfd);		/* Implicit unlock. */
***************
*** 289,295 ****
  void
  usage()
  {
! 	err(FATAL, "usage: mail.local [-f from] user ...");
  }
  
  #if __STDC__
--- 313,319 ----
  void
  usage()
  {
! 	err(FATAL, "usage: mail.local [-f from] [-q] user ...");
  }
  
  #if __STDC__
>Audit-Trail:
>Unformatted: