summaryrefslogblamecommitdiffstats
path: root/mount/mount.c
blob: 39ccd3e634cc1fa7dc7339d5653fef14d4cc04b6 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11



                                                






                                                                            
                                                                            

                                                                         
                                                                    
  
                                  


                                                                          






                                                                    






                                                              

                                                      

                                                       
                                                       

   




                   
                  
 


                





                      
                            
                     
                   


                    
                                                    
                                


                               
                
 


                      
                         

      
                                
                    

                                             
                      
 
                                       
                        



                               


                               
                                         
                         



                               
                                             



                                                           
 





                                                           
                            
                    

                                                                             
                
                                                 
                                                                




                                                                           
                                                          
                                  

                                  
                                  
                                  

                                                              
                                                            

                                                                            
                                                               
 
                                                           

                                                      


                                                                
                                         











                                                                          
                                                                            

                                                                             

                                                                           

                                                                           

                                                                               

                            





                                                                   
      


                                                                               
      




                                                                       



                                                                            


                                 

                                                                     
 
                              







                                        
                              
                   

  






















                                             
                  


                                
                                     

                     




                                                         

                                 
                                                              


                                             
                   



                                                                    
                               
                                
 



                                                         

              


 




                                                                         
                  
                                                          


                                           
                                 



                             

                                                         
                             

                                               





                                                

               
 

                            






















                                                        



                                                                        
                                                                               
           
                                                        
                  
 

                           
 
                            
 


                                                           
 



                                                                             
 






                                     



                                               
                                                                       
                           
                                 
                 

                                               

                                             


                                                                 
                                                     
                         







                                                                       


                                                       


                  
          
                                              
                         

                
                                        
                                                                          


                                             
                                                                  
                                    



                
 






                                     
               


              
                                     




                                                              


                                                                  
                                                                    
                        

                                                          
                                                               

                                        




                                                         
   
                                                                            




                                                                
                     






                                         




                                                            
          
                                           

                                                              




                       





                                                                
          

                                                            
                                                                               
   
                                                

                   


                                                            
                                         
                                                 





                                                                  
 

                                       
                                      
    
 
                                                    

 
  

                                                  
   

                                                             
             



                                                                       

                                           

                             
                                     
                                            
                                        








                                                                        
                                             
                                   
                                              
              
                                                                                 
       

                                
   
 


                          
 



                                                        
 
    




                                                                          
     
 
                         
 
                                                      
                 
                 
                                                     



                           
                                              
        
                          
   
 

                                                                           
 

                      

                  
                                                                  
            
                                        
 


                                             

                                                          
                                                                        
                                                             
                                                                            
                    
                                                              
                       

                      
                                                             
                       
                 
                            
     

   

           
 


                                                                 

                      



                                         





                                                                       
                             















































                                                                          

                   







                                                                      
                   
                                                


                                                                  
     
                          
      
     

                                                  

                           




                                          
                                                                













                                                 
                                   



                                                                         



                                                                     

        













































                                                                              






                                                                          
                     
   


















                                                                             
 

                            
            

                                                                            

                              
                                                                            


                                






                                                                               


                                  
   
 

                  


                       

                              











                                                                              
                                               
 










                                                                   
               
                           
                                                                  
                                                                            
               
                                                      
            
                                                           

               
                               
                                             


                                                                               
                                                           
                                                                           
                                                    
              
                                                                      

                             

                
                                 
                                                                   
                                     
                                                                           

                                     
                                                                      
            

                           
       
            
                 








                                                                          
            
                

                
                   

                               
                                                                        
              

                                                                            


                                                                  
                                                              







                                                                               
                                                                              
                                                                      
             
                    










                                                                         



            
                
                                           
             
                                                                 
                



                                                                    


                          
                                                                     









                                                                
                                            
                                                             
                                                                             
                                                        

                      
                                                                                  

                           
     
                 
                                                  
                                                                            
                                        

                                                                              
                                        
                                                                           
                       
          
                                                           
            
               
                                                                      
                                                          
                                              
                                                  

                                                         
                           
                



                                                                                  
              



                          
                    


                                                                  



                                            
                                                                        
                           
                                                                            
       
            
     


                                                     
   
                 

 





                                                                      
                                

                     
                                     


      
































                                                                            
          

                                                                            

              




                                              






                                                  











                                                  

                                                                     
                                                                
                     
                                                              


                                                           
                              


                                       

                                                            






                                                                 
                                                                    





                             
                                                    







                                                                 
                                                                     
                         
                                                  


                                                      

                                                               
                                  
                                








                                                   
 

                                
 


                                                                            

                        

 



                                                                             
                                          
 
          
                                              
                                       
                    


                         
                      



                                    

                     

                                                     
                                                         

                            





                                                               
                                
                                                                       
                                                                 



                                                                       

                                  
                                                                     




                                                                                

                                                                  






                                                     
                                                                   
                                                                           







                                                              
                                           












                                                 



                                                                         






                                                        

                                                                         
                                                        

























                                                    


                      



















                                    

  

                                                                      
           

























                                                                               


   
                               

















                                                                            
 
                     
                                  

      












































































                                                                                
 

                       
 
                                                                            
 




                                                 
 





                                                                             
 





                                                                         
 




















































                                                                            
 























                                                                           
      


                                         
 


                                
 
/*
 * A mount(8) for Linux 0.99.
 * mount.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp
 *
 * Wed Sep 14 22:43:00 1994: Mitchum DSouza
 * (mitch@mrc-applied-psychology.cambridge.ac.uk) added support for mounting
 * the "loop" device.
 *
 * Wed Sep 14 22:55:10 1994: Sander van Malssen (svm@kozmix.hacktic.nl)
 * added support for remounting readonly file systems readonly.
 *
 * Wed Feb  8 12:27:00 1995: Andries.Brouwer@cwi.nl fixed up error messages.
 * Sat Jun  3 20:44:38 1995: Patches from Andries.Brouwer@cwi.nl applied.
 * Tue Sep 26 22:38:20 1995: aeb@cwi.nl, many changes
 * Fri Feb 23 13:47:00 1996: aeb@cwi.nl, loop device related changes
 *
 * Since then, many changes - aeb.
 *
 * Wed Oct  1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
 * Implemented the "bg", "fg" and "retry" mount options for NFS.
 *
 * Tue Aug  4 15:54:31 1998: aeb@cwi.nl:
 * Open fd 0,1,2 so that printf's do not clobber /etc/mtab or so.
 * Mangle filenames with embedded spaces. Add ufsmagic. Add locking.
 * Avoid unnecessary error messages about /proc.
 * Improve support for noncanonical names in /etc/fstab.
 * Add support for volume labels and UUIDs.
 *
 * 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
 * - added Native Language Support
 * 1999-03-21 Arnaldo Carvalho de Melo <acme@conectiva.com.br>
 * - fixed strerr(errno) in gettext calls
 * 1999-07-05 Hirokazu Takahashi <h-takaha@sss.abk.nec.co.jp>
 * - fixed use of nouser option
 * 1999-09-09 Michael K. Johnson <johnsonm@redhat.com>
 * - added `owner' mount option
 * 2000-05-11 Mark A. Peloquin <peloquin@us.ibm.com>
 * - check_special_mountprog now returns correct status
 * 2000-11-08 aeb: accept nonnumeric uid=, gid= options
 */

#include <unistd.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <getopt.h>
#include <stdio.h>

#include <pwd.h>
#include <grp.h>

#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/mount.h>

#include "mount_constants.h"
#include "sundries.h"
#include "mntent.h"
#include "fstab.h"
#include "lomount.h"
#include "loop.h"
#include "linux_fs.h"		/* for BLKGETSIZE */
#include "mount_guess_rootdev.h"
#include "mount_guess_fstype.h"
#include "mount_by_label.h"
#include "getusername.h"
#include "nls.h"

#define DO_PS_FIDDLING

#ifdef DO_PS_FIDDLING
#include "setproctitle.h"
#endif

/* True for fake mount (-f).  */
static int fake = 0;

/* Don't write a entry in /etc/mtab (-n).  */
static int nomtab = 0;

/* True for explicit readonly (-r).  */
static int readonly = 0;

/* Nonzero for chatty (-v).  */
int verbose = 0;

/* Nonzero for sloppy (-s).  */
int sloppy = 0;

/* True for explicit read/write (-w).  */
static int readwrite = 0;

/* True for all mount (-a).  */
int all = 0;

/* True for fork() during all mount (-F).  */
static int optfork = 0;

/* Add volumelabel in a listing of mounted devices (-l). */
static int list_with_volumelabel = 0;

/* Nonzero for mount --bind */
static int bind = 0;

/* Nonzero for mount {--replace|--before|--after|--over} */
static int mounttype = 0;

/* True if ruid != euid.  */
static int suid = 0;

/* Map from -o and fstab option strings to the flag argument to mount(2).  */
struct opt_map {
  const char *opt;		/* option name */
  int  skip;			/* skip in mtab option string */
  int  inv;			/* true if flag value should be inverted */
  int  mask;			/* flag mask value */
};

/* Custom mount options for our own purposes.  */
/* Maybe these should now be freed for kernel use again */
#define MS_NOAUTO	0x80000000
#define MS_USERS	0x40000000
#define MS_USER		0x20000000
#define MS_OWNER	0x10000000
#define MS_LOOP		0x00010000

/* Options that we keep the mount system call from seeing.  */
#define MS_NOSYS	(MS_NOAUTO|MS_USERS|MS_USER|MS_LOOP)

/* Options that we keep from appearing in the options field in the mtab.  */
#define MS_NOMTAB	(MS_REMOUNT|MS_NOAUTO|MS_USERS|MS_USER)

/* Options that we make ordinary users have by default.  */
#define MS_SECURE	(MS_NOEXEC|MS_NOSUID|MS_NODEV)

/* Options that we make owner-mounted devices have by default */
#define MS_OWNERSECURE	(MS_NOSUID|MS_NODEV)

static const struct opt_map opt_map[] = {
  { "defaults",	0, 0, 0		},	/* default options */
  { "ro",	1, 0, MS_RDONLY	},	/* read-only */
  { "rw",	1, 1, MS_RDONLY	},	/* read-write */
  { "exec",	0, 1, MS_NOEXEC	},	/* permit execution of binaries */
  { "noexec",	0, 0, MS_NOEXEC	},	/* don't execute binaries */
  { "suid",	0, 1, MS_NOSUID	},	/* honor suid executables */
  { "nosuid",	0, 0, MS_NOSUID	},	/* don't honor suid executables */
  { "dev",	0, 1, MS_NODEV	},	/* interpret device files  */
  { "nodev",	0, 0, MS_NODEV	},	/* don't interpret devices */
  { "sync",	0, 0, MS_SYNCHRONOUS},	/* synchronous I/O */
  { "async",	0, 1, MS_SYNCHRONOUS},	/* asynchronous I/O */
  { "remount",  0, 0, MS_REMOUNT},      /* Alter flags of mounted FS */
  { "bind",	0, 0, MS_BIND   },	/* Remount part of tree elsewhere */
  { "auto",	0, 1, MS_NOAUTO	},	/* Can be mounted using -a */
  { "noauto",	0, 0, MS_NOAUTO	},	/* Can  only be mounted explicitly */
  { "users",	0, 0, MS_USERS	},	/* Allow ordinary user to mount */
  { "nousers",	0, 1, MS_USERS	},	/* Forbid ordinary user to mount */
  { "user",	0, 0, MS_USER	},	/* Allow ordinary user to mount */
  { "nouser",	0, 1, MS_USER	},	/* Forbid ordinary user to mount */
  { "owner",	0, 0, MS_OWNER  },	/* Let the owner of the device mount */
  { "noowner",	0, 1, MS_OWNER  },	/* Device owner has no special privs */
  /* add new options here */
#ifdef MS_NOSUB
  { "sub",	0, 1, MS_NOSUB	},	/* allow submounts */
  { "nosub",	0, 0, MS_NOSUB	},	/* don't allow submounts */
#endif
#ifdef MS_SILENT
  { "quiet",	0, 0, MS_SILENT    },	/* be quiet  */
  { "loud",	0, 1, MS_SILENT    },	/* print out messages. */
#endif
#ifdef MS_MANDLOCK
  { "mand",	0, 0, MS_MANDLOCK },	/* Allow mandatory locks on this FS */
  { "nomand",	0, 1, MS_MANDLOCK },	/* Forbid mandatory locks on this FS */
#endif
  { "loop",	1, 0, MS_LOOP	},	/* use a loop device */
#ifdef MS_NOATIME
  { "atime",	0, 1, MS_NOATIME },	/* Update access time */
  { "noatime",	0, 0, MS_NOATIME },	/* Do not update access time */
#endif
#ifdef MS_NODIRATIME
  { "diratime",	0, 1, MS_NODIRATIME },	/* Update dir access times */
  { "nodiratime", 0, 0, MS_NODIRATIME },/* Do not update dir access times */
#endif
  { NULL,	0, 0, 0		}
};

static char *opt_loopdev, *opt_vfstype, *opt_offset, *opt_encryption,
  *opt_speed;

static struct string_opt_map {
  char *tag;
  int skip;
  char **valptr;
} string_opt_map[] = {
  { "loop=",	0, &opt_loopdev },
  { "vfs=",	1, &opt_vfstype },
  { "offset=",	0, &opt_offset },
  { "encryption=", 0, &opt_encryption },
  { "speed=", 0, &opt_speed },
  { NULL, 0, NULL }
};

static void
clear_string_opts(void) {
  struct string_opt_map *m;

  for (m = &string_opt_map[0]; m->tag; m++)
    *(m->valptr) = NULL;
}

static int
parse_string_opt(char *s) {
  struct string_opt_map *m;
  int lth;

  for (m = &string_opt_map[0]; m->tag; m++) {
    lth = strlen(m->tag);
    if (!strncmp(s, m->tag, lth)) {
      *(m->valptr) = xstrdup(s + lth);
      return 1;
    }
  }
  return 0;
}

int mount_quiet=0;

/* Report on a single mount.  */
static void
print_one (const struct mntent *me) {
     if (mount_quiet)
	  return;
     printf ("%s on %s", me->mnt_fsname, me->mnt_dir);
     if (me->mnt_type != NULL && *(me->mnt_type) != '\0')
	  printf (" type %s", me->mnt_type);
     if (me->mnt_opts != NULL)
	  printf (" (%s)", me->mnt_opts);
     if (list_with_volumelabel) {
	     const char *label;
	     label = get_volume_label_by_spec(me->mnt_fsname);
	     if (label)
		     printf (" [%s]", label);
     }
     printf ("\n");
}

/* Report on everything in mtab (of the specified types if any).  */
static int
print_all (string_list types) {
     struct mntentchn *mc, *mc0;

     mc0 = mtab_head();
     for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) {
	  if (matching_type (mc->m.mnt_type, types))
	       print_one (&(mc->m));
     }
     exit (0);
}


/*
 * Look for OPT in opt_map table and return mask value.
 * If OPT isn't found, tack it onto extra_opts (which is non-NULL).
 * For the options uid= and gid= replace user or group name by its value.
 */
static inline void
parse_opt (const char *opt, int *mask, char *extra_opts) {
  const struct opt_map *om;

  for (om = opt_map; om->opt != NULL; om++)
      if (streq (opt, om->opt)) {
	if (om->inv)
	  *mask &= ~om->mask;
	else
	  *mask |= om->mask;
	if ((om->mask == MS_USER || om->mask == MS_USERS)
	    && !om->inv)
	  *mask |= MS_SECURE;
	if ((om->mask == MS_OWNER) && !om->inv)
	  *mask |= MS_OWNERSECURE;
#ifdef MS_SILENT
        if (om->mask == MS_SILENT && om->inv)  {
          mount_quiet = 1;
          verbose = 0;
        }
#endif
	return;
      }

  if (*extra_opts)
    strcat(extra_opts, ",");

  /* convert nonnumeric ids to numeric */
  if (!strncmp(opt, "uid=", 4) && !isdigit(opt[4])) {
	  struct passwd *pw = getpwnam(opt+4);
	  char uidbuf[20];

	  if (pw) {
		  sprintf(uidbuf, "uid=%d", pw->pw_uid);
		  strcat(extra_opts, uidbuf);
		  return;
	  }
  }
  if (!strncmp(opt, "gid=", 4) && !isdigit(opt[4])) {
	  struct group *gr = getgrnam(opt+4);
	  char gidbuf[20];

	  if (gr) {
		  sprintf(gidbuf, "gid=%d", gr->gr_gid);
		  strcat(extra_opts, gidbuf);
		  return;
	  }
  }

  strcat(extra_opts, opt);
}
  
/* Take -o options list and compute 4th and 5th args to mount(2).  flags
   gets the standard options (indicated by bits) and extra_opts all the rest */
static void
parse_opts (char *opts, int *flags, char **extra_opts) {
	char *opt;

	*flags = 0;
	*extra_opts = NULL;

	clear_string_opts();

	if (opts != NULL) {
		*extra_opts = xmalloc (strlen (opts) + 1); 
		**extra_opts = '\0';

		for (opt = strtok (opts, ","); opt; opt = strtok (NULL, ","))
			if (!parse_string_opt (opt))
				parse_opt (opt, flags, *extra_opts);
	}

	if (readonly)
		*flags |= MS_RDONLY;
	if (readwrite)
		*flags &= ~MS_RDONLY;
	*flags |= mounttype;
	if (bind)
		*flags |= MS_BIND;
}

/* Try to build a canonical options string.  */
static char *
fix_opts_string (int flags, const char *extra_opts, const char *user) {
  const struct opt_map *om;
  const struct string_opt_map *m;
  char *new_opts;

  new_opts = (flags & MS_RDONLY) ? "ro" : "rw";
  for (om = opt_map; om->opt != NULL; om++) {
      if (om->skip)
	continue;
      if (om->inv || !om->mask || (flags & om->mask) != om->mask)
	continue;
      new_opts = xstrconcat3(new_opts, ",", om->opt);
      flags &= ~om->mask;
  }
  for (m = &string_opt_map[0]; m->tag; m++) {
      if (!m->skip && *(m->valptr))
	   new_opts = xstrconcat4(new_opts, ",", m->tag, *(m->valptr));
  }
  if (extra_opts && *extra_opts) {
      new_opts = xstrconcat3(new_opts, ",", extra_opts);
  }
  if (user) {
      new_opts = xstrconcat3(new_opts, ",user=", user);
  }
  return new_opts;
}

static int
already (const char *spec, const char *node) {
    struct mntentchn *mc;
    int ret = 1;

    if ((mc = getmntfile(node)) != NULL)
        error (_("mount: according to mtab, %s is already mounted on %s"),
	       mc->m.mnt_fsname, node);
    else if (spec && strcmp (spec, "none") &&
	     (mc = getmntfile(spec)) != NULL)
        error (_("mount: according to mtab, %s is mounted on %s"),
	       spec, mc->m.mnt_dir);
    else
        ret = 0;
    return ret;
}

/* Create mtab with a root entry.  */
static void
create_mtab (void) {
  struct mntentchn *fstab;
  struct mntent mnt;
  int flags;
  char *extra_opts;
  mntFILE *mfp;

  lock_mtab();

  mfp = my_setmntent (MOUNTED, "a+");
  if (mfp == NULL || mfp->mntent_fp == NULL) {
    int errsv = errno;
    die (EX_FILEIO, _("mount: can't open %s for writing: %s"),
	 MOUNTED, strerror (errsv));
  }

  /* Find the root entry by looking it up in fstab */
  if ((fstab = getfsfile ("/")) || (fstab = getfsfile ("root"))) {
      parse_opts (xstrdup (fstab->m.mnt_opts), &flags, &extra_opts);
      mnt.mnt_dir = "/";
      mnt.mnt_fsname = canonicalize (fstab->m.mnt_fsname);
      mnt.mnt_type = fstab->m.mnt_type;
      mnt.mnt_opts = fix_opts_string (flags, extra_opts, NULL);
      mnt.mnt_freq = mnt.mnt_passno = 0;

      if (my_addmntent (mfp, &mnt) == 1) {
        int errsv = errno;
	die (EX_FILEIO, _("mount: error writing %s: %s"),
	     MOUNTED, strerror (errsv));
      }
  }
  if (fchmod (fileno (mfp->mntent_fp), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0)
    if (errno != EROFS) {
      int errsv = errno;
      die (EX_FILEIO, _("mount: error changing mode of %s: %s"),
	   MOUNTED, strerror (errsv));
    }
  my_endmntent (mfp);

  unlock_mtab();
}

/* count successful mount system calls */
static int mountcount = 0;

/*
 * do_mount_syscall()
 *	Mount a single file system. Keep track of successes.
 * returns: 0: OK, -1: error in errno
 */
static int
do_mount_syscall (struct mountargs *args) {
     int ret = mount (args->spec, args->node, args->type,
		      MS_MGC_VAL | (args->flags), args->data);
     if (ret == 0)
	  mountcount++;
     return ret;
}

/*
 * guess_fstype_and_mount()
 *	Mount a single file system. Guess the type when unknown.
 * returns: 0: OK, -1: error in errno, 1: other error
 *	don't exit on non-fatal errors.
 */
static int
guess_fstype_and_mount (char *spec, char *node, char **type,
			int flags, char *mount_opts) {
   struct mountargs args = { spec, node, NULL, flags & ~MS_NOSYS, mount_opts };
   
   if (*type && strcasecmp (*type, "auto") == 0)
      *type = NULL;

   if (!*type && (flags & MS_BIND))
      *type = "none";		/* random, but not "bind" */

   if (!*type && !(flags & MS_REMOUNT)) {
      *type = guess_fstype_from_superblock(spec);
      if (*type && !strcmp(*type, "swap")) {
	  error(_("%s looks like swapspace - not mounted"), spec);
	  *type = NULL;
	  return 1;
      }
   }

   if (*type || (flags & MS_REMOUNT)) {
      args.type = *type;
      return do_mount_syscall (&args);
   }

   return procfsloop(do_mount_syscall, &args, type);
}

/*
 * suid_check()
 *	Die if the user is not allowed to do this.
 */
static void
suid_check(char *spec, char *node, int *flags, char **user) {
  if (suid) {
      /* RedHat patch: allow owners to mount when fstab contains
	 the owner option.  Note that this should never be used
         in a high security environment, but may be useful to give
         people at the console the possibility of mounting a floppy. */
      if (*flags & MS_OWNER) {
	  if (!strncmp(spec, "/dev/", 5)) {
	      struct stat sb;

	      if (!stat(spec, &sb)) {
		  if (getuid() == sb.st_uid)
		      *flags |= MS_USER;
	      }
	  }
      }
      /* James Kehl <mkehl@gil.com.au> came with a similar patch:
	 allow an arbitrary user to mount when he is the owner of
	 the mount-point and has write-access to the device.
         This is even less secure. Let me skip it for the time being;
         there should be an explicit fstab line allowing such things. */

      if (!(*flags & (MS_USER | MS_USERS))) {
          if (already (spec, node))
	    die (EX_USAGE, _("mount failed"));
	  else
            die (EX_USAGE, _("mount: only root can mount %s on %s"), spec, node);
      }
      if (*flags & MS_USER)
	  *user = getusername();
  }

  if (*flags & MS_OWNER)
      *flags &= ~MS_OWNER;
}

static int
loop_check(char **spec, char **type, int *flags,
	   int *loop, char **loopdev, char **loopfile) {
  int looptype, offset;

  /*
   * In the case of a loop mount, either type is of the form lo@/dev/loop5
   * or the option "-o loop=/dev/loop5" or just "-o loop" is given, or
   * mount just has to figure things out for itself from the fact that
   * spec is not a block device. We do not test for a block device
   * immediately: maybe later other types of mountable objects will occur.
   */

  *loopdev = opt_loopdev;

  looptype = (*type && strncmp("lo@", *type, 3) == 0);
  if (looptype) {
    if (*loopdev)
      error(_("mount: loop device specified twice"));
    *loopdev = *type + 3;
    *type = opt_vfstype;
  } else if (opt_vfstype) {
    if (*type)
      error(_("mount: type specified twice"));
    else
      *type = opt_vfstype;
  }

  *loop = ((*flags & MS_LOOP) || *loopdev || opt_offset || opt_encryption);
  *loopfile = *spec;

  if (*loop) {
    *flags |= MS_LOOP;
    if (fake) {
      if (verbose)
	printf(_("mount: skipping the setup of a loop device\n"));
    } else {
      int loopro = (*flags & MS_RDONLY);

      if (!*loopdev || !**loopdev)
	*loopdev = find_unused_loop_device();
      if (!*loopdev)
	return EX_SYSERR;	/* no more loop devices */
      if (verbose)
	printf(_("mount: going to use the loop device %s\n"), *loopdev);
      offset = opt_offset ? strtoul(opt_offset, NULL, 0) : 0;
      if (set_loop (*loopdev, *loopfile, offset, opt_encryption, &loopro)) {
	if (verbose)
	  printf(_("mount: failed setting up loop device\n"));
	return EX_FAIL;
      }
      if (verbose > 1)
	printf(_("mount: setup loop device successfully\n"));
      *spec = *loopdev;
      if (loopro)
	*flags |= MS_RDONLY;
    }
  }

  return 0;
}

static void
update_mtab_entry(char *spec, char *node, char *type, char *opts,
		  int flags, int freq, int pass) {
    struct mntent mnt;

    mnt.mnt_fsname = canonicalize (spec);
    mnt.mnt_dir = canonicalize (node);
    mnt.mnt_type = type;
    mnt.mnt_opts = opts;
    mnt.mnt_freq = freq;
    mnt.mnt_passno = pass;
      
    /* We get chatty now rather than after the update to mtab since the
       mount succeeded, even if the write to /etc/mtab should fail.  */
    if (verbose)
	    print_one (&mnt);

    if (!nomtab && mtab_is_writable()) {
	if (flags & MS_REMOUNT)
	    update_mtab (mnt.mnt_dir, &mnt);
	else {
	    mntFILE *mfp;

	    lock_mtab();
	    mfp = my_setmntent(MOUNTED, "a+");
	    if (mfp == NULL || mfp->mntent_fp == NULL) {
	       	int errsv = errno;
		error(_("mount: can't open %s: %s"), MOUNTED,
		      strerror (errsv));
	    } else {
		if ((my_addmntent (mfp, &mnt)) == 1) {
			int errsv = errno;
			error(_("mount: error writing %s: %s"), MOUNTED,
			      strerror (errsv));
		}
		my_endmntent(mfp);
	    }
	    unlock_mtab();
	}
    }
}

static void
cdrom_setspeed(char *spec) {
#define CDROM_SELECT_SPEED      0x5322  /* Set the CD-ROM speed */
    if (opt_speed) {
	int cdrom;
	int speed = atoi(opt_speed);

	if ((cdrom = open(spec, O_RDONLY | O_NONBLOCK)) < 0)
	    die(EX_FAIL, _("mount: cannot not open %s for setting speed"),
		spec);
	if (ioctl(cdrom, CDROM_SELECT_SPEED, speed) < 0)
	    die(EX_FAIL, _("mount: cannot set speed: %s"),
		strerror(errno));
	close(cdrom);
    }
}

/*
 * check_special_mountprog()
 *	If there is a special mount program for this type, exec it.
 * returns: 0: no exec was done, 1: exec was done, status has result
 */
#define ALWAYS_STAT

static int
check_special_mountprog(char *spec, char *node, char *type, int flags,
			char *extra_opts, int *status) {
  char mountprog[120];
  struct stat statbuf;
  int res;

  if (type &&
#ifndef ALWAYS_STAT
     (streq (type, "smb") || streq (type, "ncp")
      /* these are incorrect but perhaps used by smbmount or so */
       || streq (type, "smbfs") || streq (type, "ncpfs")
     )
#else
     (strlen (type) < 100)
#endif
  ) {
       sprintf(mountprog, "/sbin/mount.%s", type);
       if (stat(mountprog, &statbuf) == 0) {
	    res = fork();
	    if (res == 0) {
		 char *oo, *mountargs[10];
		 int i = 0;

		 setuid(getuid());
		 setgid(getgid());
		 oo = fix_opts_string (flags, extra_opts, NULL);
		 mountargs[i++] = mountprog;
		 mountargs[i++] = spec;
		 mountargs[i++] = node;
		 if (nomtab)
		      mountargs[i++] = "-n";
		 if (verbose)
		      mountargs[i++] = "-v";
		 if (oo && *oo) {
		      mountargs[i++] = "-o";
		      mountargs[i++] = oo;
		 }
		 mountargs[i] = NULL;
		 execv(mountprog, mountargs);
		 exit(1);	/* exec failed */
	    } else if (res != -1) {
		 int st;
		 wait(&st);
		 *status = (WIFEXITED(st) ? WEXITSTATUS(st) : EX_SYSERR);
		 return 1;
	    } else {
	    	 int errsv = errno;
		 error(_("mount: cannot fork: %s"), strerror(errsv));
	    }
       }
  }
  return 0;
}

/*
 * try_mount_one()
 *	Try to mount one file system. When "bg" is 1, this is a retry
 *	in the background. One additional exit code EX_BG is used here.
 *	It is used to instruct the caller to retry the mount in the
 *	background.
 * returns: 0: OK, EX_SYSERR, EX_FAIL, return code from nfsmount,
 *      return status from wait
 */
static int
try_mount_one (const char *spec0, const char *node0, char *type0,
	       const char *opts0, int freq, int pass, int bg, int ro) {
  int res, status;
  int mnt5_res = 0;		/* only for gcc */
  int mnt_err;
  int flags;
  char *extra_opts;		/* written in mtab */
  char *mount_opts;		/* actually used on system call */
  const char *opts;
  char *spec, *node, *type;
  char *user = 0;
  int loop = 0;
  char *loopdev = 0, *loopfile = 0;
  struct stat statbuf;

  spec = xstrdup(spec0);
  node = xstrdup(node0);
  type = xstrdup(type0);
  opts = xstrdup(opts0);

  parse_opts (xstrdup (opts), &flags, &extra_opts);

  suid_check (spec, node, &flags, &user);

  /* quietly succeed for fstab entries that don't get mounted automatically */
  if (all && (flags & MS_NOAUTO))
    return 0;

  mount_opts = extra_opts;

  if (opt_speed)
      cdrom_setspeed(spec);

  if (!(flags & MS_REMOUNT)) {
      /* don't set up a (new) loop device if we only remount - this left
       * stale assignments of files to loop devices. Nasty when used for
       * encryption.
       */
      res = loop_check (&spec, &type, &flags, &loop, &loopdev, &loopfile);
      if (res)
	  return res;
  }

  /*
   * Call mount.TYPE for types that require a separate mount program.
   * For the moment these types are ncp and smb.
   */
  if (check_special_mountprog (spec, node, type, flags, extra_opts, &status))
      return status;

  if (!fake && type && streq (type, "nfs")) {
#ifdef HAVE_NFS
retry_nfs:
    mnt_err = nfsmount (spec, node, &flags, &extra_opts, &mount_opts, bg);
    if (mnt_err)
      return mnt_err;
#else
    die (EX_SOFTWARE, _("mount: this version was compiled "
	              "without support for the type `nfs'"));
#endif
  }

  block_signals (SIG_BLOCK);

  if (!fake)
    mnt5_res = guess_fstype_and_mount (spec, node, &type, flags & ~MS_NOSYS,
				       mount_opts);

  if (fake || mnt5_res == 0) {
      /* Mount succeeded, report this (if verbose) and write mtab entry.  */
      if (loop)
	  opt_loopdev = loopdev;

      update_mtab_entry(loop ? loopfile : spec,
			node,
			type ? type : "unknown",
			fix_opts_string (flags & ~MS_NOMTAB, extra_opts, user),
			flags,
			freq,
			pass);

      block_signals (SIG_UNBLOCK);
      return 0;
  }

  mnt_err = errno;

  if (loop)
	del_loop(spec);

  block_signals (SIG_UNBLOCK);

#ifdef HAVE_NFS
  if (mnt_err && type && streq (type, "nfs")) {
      extern int nfs_mount_version;
      if (nfs_mount_version == 4) {
	  if (verbose)
	    printf(_("mount: failed with nfs mount version 4, trying 3..\n"));
	  nfs_mount_version = 3;
	  goto retry_nfs;
      }
  }
#endif

  /* Mount failed, complain, but don't die.  */

  if (type == 0) {
    if (suid)
      error (_("mount: I could not determine the filesystem type, "
	       "and none was specified"));
    else
      error (_("mount: you must specify the filesystem type"));
  } else if (mnt5_res != -1) {
      /* should not happen */
      error (_("mount: mount failed"));
  } else {
   switch (mnt_err) {
    case EPERM:
      if (geteuid() == 0) {
	   if (stat (node, &statbuf) || !S_ISDIR(statbuf.st_mode))
		error (_("mount: mount point %s is not a directory"), node);
	   else
		error (_("mount: permission denied"));
      } else
	error (_("mount: must be superuser to use mount"));
      break;
    case EBUSY:
      if (flags & MS_REMOUNT) {
	error (_("mount: %s is busy"), node);
      } else if (!strcmp(type, "proc") && !strcmp(node, "/proc")) {
	/* heuristic: if /proc/version exists, then probably proc is mounted */
	if (stat ("/proc/version", &statbuf))   /* proc mounted? */
	   error (_("mount: %s is busy"), node);   /* no */
	else if(!all || verbose)                /* yes, don't mention it */
	   error (_("mount: proc already mounted"));
      } else {
	error (_("mount: %s already mounted or %s busy"), spec, node);
	already (spec, node);
      }
      break;
    case ENOENT:
      if (lstat (node, &statbuf))
	   error (_("mount: mount point %s does not exist"), node);
      else if (stat (node, &statbuf))
	   error (_("mount: mount point %s is a symbolic link to nowhere"),
		  node);
      else if (stat (spec, &statbuf))
	   error (_("mount: special device %s does not exist"), spec);
      else {
           errno = mnt_err;
           perror("mount");
      }
      break;
    case ENOTDIR:
      if (stat (node, &statbuf) || ! S_ISDIR(statbuf.st_mode))
           error (_("mount: mount point %s is not a directory"), node);
      else if (stat (spec, &statbuf) && errno == ENOTDIR)
	   error (_("mount: special device %s does not exist\n"
		    "       (a path prefix is not a directory)\n"), spec);
      else {
	   errno = mnt_err;
	   perror("mount");
      }
      break;
    case EINVAL:
    { int fd;
      long size;
      int warned=0;

      if (flags & MS_REMOUNT) {
	error (_("mount: %s not mounted already, or bad option"), node);
      } else {
	error (_("mount: wrong fs type, bad option, bad superblock on %s,\n"
	       "       or too many mounted file systems"),
	       spec);

	if (stat (spec, &statbuf) == 0 && S_ISBLK(statbuf.st_mode)
	   && (fd = open(spec, O_RDONLY | O_NONBLOCK)) >= 0) {
	  if(ioctl(fd, BLKGETSIZE, &size) == 0) {
	    if (size == 0) {
	      warned++;
	  error ("       (could this be the IDE device where you in fact use\n"
		 "       ide-scsi so that sr0 or sda or so is needed?)");
	    }
	    if (size && size <= 2) {
	      warned++;
	  error ("       (aren't you trying to mount an extended partition,\n"
		 "       instead of some logical partition inside?)");
	    }
	  close(fd);
	  }
#if 0
	  /* 0xf for SCSI, 0x3f for IDE. One might check /proc/partitions
	     to see whether this thing really is partitioned.
	     Do not suggest partitions for /dev/fd0. */
	  if (!warned && (statbuf.st_rdev & 0xf) == 0) {
	    warned++;
	    error ("       (could this be the whole disk device\n"
		   "       where you need a partition?)");
	  }
#endif
	}
      }
      break;
    }
    case EMFILE:
      error (_("mount table full")); break;
    case EIO:
      error (_("mount: %s: can't read superblock"), spec); break;
    case ENODEV:
    { int pfs;
      if ((pfs = is_in_procfs(type)) == 1 || !strcmp(type, "guess"))
        error(_("mount: %s: unknown device"), spec);
      else if (pfs == 0) {
	char *lowtype, *p;
	int u;

	error (_("mount: fs type %s not supported by kernel"), type);

	/* maybe this loser asked for FAT or ISO9660 or isofs */
	lowtype = xstrdup(type);
	u = 0;
	for(p=lowtype; *p; p++) {
	  if(tolower(*p) != *p) {
	    *p = tolower(*p);
	    u++;
	  }
	}
	if (u && is_in_procfs(lowtype) == 1)
	  error (_("mount: probably you meant %s"), lowtype);
	else if (!strncmp(lowtype, "iso", 3) && is_in_procfs("iso9660") == 1)
	  error (_("mount: maybe you meant iso9660 ?"));
	free(lowtype);
      } else
	error (_("mount: %s has wrong device number or fs type %s not supported"),
	       spec, type);
      break;
    }
    case ENOTBLK:
      if (stat (spec, &statbuf)) /* strange ... */
	error (_("mount: %s is not a block device, and stat fails?"), spec);
      else if (S_ISBLK(statbuf.st_mode))
        error (_("mount: the kernel does not recognize %s as a block device\n"
	       "       (maybe `insmod driver'?)"), spec);
      else if (S_ISREG(statbuf.st_mode))
	error (_("mount: %s is not a block device (maybe try `-o loop'?)"),
		 spec);
      else
	error (_("mount: %s is not a block device"), spec);
      break;
    case ENXIO:
      error (_("mount: %s is not a valid block device"), spec); break;
    case EACCES:  /* pre-linux 1.1.38, 1.1.41 and later */
    case EROFS:   /* linux 1.1.38 and later */
    { char *bd = (loop ? "" : _("block device "));
      if (ro || (flags & MS_RDONLY)) {
          error (_("mount: cannot mount %s%s read-only"),
		 bd, spec);
          break;
      } else if (readwrite) {
	  error (_("mount: %s%s is write-protected but explicit `-w' flag given"),
		 bd, spec);
	  break;
      } else {
	 if (loop) {
	     opts = opts0;
	     type = type0;
	 }
         if (opts) {
	     char *opts1 = realloc(xstrdup(opts), strlen(opts)+4);
             strcat(opts1, ",ro");
	     opts = opts1;
         } else
             opts = "ro";
	 if (type && !strcmp(type, "guess"))
	     type = 0;
         error (_("mount: %s%s is write-protected, mounting read-only"),
		bd, spec0);
	 return try_mount_one (spec0, node0, type, opts, freq, pass, bg, 1);
      }
      break;
    }
    default:
      error ("mount: %s", strerror (mnt_err)); break;
    }
  }
  return EX_FAIL;
}

/*
 * set_proc_name()
 *	Update the argument vector, so that this process may be easily
 *	identified in a "ps" listing.
 */
static void
set_proc_name (const char *spec)
{
#ifdef DO_PS_FIDDLING
	setproctitle ("mount", spec);
#endif
}

static char *
subst_string(const char *s, const char *sub, int sublen, const char *repl) {
	char *n;

	n = (char *) xmalloc(strlen(s)-sublen+strlen(repl)+1);
	strncpy (n, s, sub-s);
	strcpy (n + (sub-s), repl);
	strcat (n, sub+sublen);
	return n;
}

static const char *
usersubst(const char *opts) {
	char *s, *w;
	char id[40];

	s = "uid=useruid";
	if (opts && (w = strstr(opts, s)) != NULL) {
		sprintf(id, "uid=%d", getuid());
		opts = subst_string(opts, w, strlen(s), id);
	}
	s = "gid=usergid";
	if (opts && (w = strstr(opts, s)) != NULL) {
		sprintf(id, "gid=%d", getgid());
		opts = subst_string(opts, w, strlen(s), id);
	}
	return opts;
}
		

/*
 * Return 0 for success (either mounted sth or -a and NOAUTO was given)
 */
static int
mount_one (const char *spec, const char *node, char *type, const char *opts,
	   char *cmdlineopts, int freq, int pass) {
  int status;
  int status2;
  int specset = 0;
  char *nspec;

  /* Substitute values in opts, if required */
  opts = usersubst(opts);

  /* Merge the fstab and command line options.  */
  if (opts == NULL)
       opts = cmdlineopts;
  else if (cmdlineopts != NULL)
       opts = xstrconcat3(opts, ",", cmdlineopts);

  if (!strncmp(spec, "UUID=", 5)) {
      nspec = get_spec_by_uuid(spec+5);
      specset = 1;
  } else if (!strncmp(spec, "LABEL=", 6)) {
      nspec = get_spec_by_volume_label(spec+6);
      specset = 2;
  } else
      nspec = 0;		/* just for gcc */

  if (specset) {
      if (nspec) {
	  spec = nspec;
	  if (verbose > 1)
		  printf(_("mount: going to mount %s by %s\n"), spec,
			 (specset==1) ? _("UUID") : _("label"));
      } else if(!all)
          die (EX_USAGE, _("mount: no such partition found"));
      /* if -a then we may be rescued by a noauto option */
  }

  if (type == NULL && !bind) {
      if (strchr (spec, ':') != NULL) {
	type = "nfs";
	if (verbose)
	  printf(_("mount: no type was given - "
		 "I'll assume nfs because of the colon\n"));
      }
  }

  /*
   * Try to mount the file system. When the exit status is EX_BG,
   * we will retry in the background. Otherwise, we're done.
   */
  status = try_mount_one (spec, node, type, opts, freq, pass, 0, 0);
  if (status != EX_BG)
    return status;

  /*
   * Retry in the background.
   */
  printf (_("mount: backgrounding \"%s\"\n"), spec);
  fflush( stdout );		/* prevent duplicate output */
  if (fork() > 0)
    return 0;			/* parent returns "success" */
  spec = xstrdup(spec);		/* arguments will be destroyed */
  node = xstrdup(node);		/* by set_proc_name()          */
  type = xstrdup(type);
  opts = xstrdup(opts);
  set_proc_name (spec);		/* make a nice "ps" listing */
  status2 = try_mount_one (spec, node, type, opts, freq, pass, 1, 0);
  if (verbose && status2)
    printf (_("mount: giving up \"%s\"\n"), spec);
  exit (0);			/* child stops here */
}

/* Check if an fsname/dir pair was already in the old mtab.  */
static int
mounted (char *spec, char *node) {
     struct mntentchn *mc, *mc0;
     char *nspec = NULL;

     if (!strncmp(spec, "UUID=", 5))
	  nspec = get_spec_by_uuid(spec+5);
     else if (!strncmp(spec, "LABEL=", 6))
	  nspec = get_spec_by_volume_label(spec+6);

     if (nspec)
	  spec = nspec;

     spec = canonicalize (spec);
     node = canonicalize (node);

     mc0 = mtab_head();
     for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
	  if (streq (spec, mc->m.mnt_fsname) && streq (node, mc->m.mnt_dir))
	       return 1;
     return 0;
}

/* Mount all filesystems of the specified types except swap and root.  */
/* With the --fork option: fork and let different incarnations of
   mount handle different filesystems.  However, try to avoid several
   simultaneous mounts on the same physical disk, since that is very slow. */
#define DISKMAJOR(m)	(((int) m) & ~0xf)

static int
mount_all (string_list types, char *options) {
     struct mntentchn *mc, *mc0, *mtmp;
     int status = 0;
     struct stat statbuf;
     struct child {
	  pid_t pid;
	  char *group;
	  struct mntentchn *mec;
	  struct mntentchn *meclast;
	  struct child *nxt;
     } childhead, *childtail, *cp;
     char major[22];
     char *g, *colon;

     /* build a chain of what we have to do, or maybe
	several chains, one for each major or NFS host */
     childhead.nxt = 0;
     childtail = &childhead;
     mc0 = fstab_head();
     for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) {
	  if (matching_type (mc->m.mnt_type, types)
	      && !streq (mc->m.mnt_dir, "/")
	      && !streq (mc->m.mnt_dir, "root")) {
	       if (mounted (mc->m.mnt_fsname, mc->m.mnt_dir)) {
		    if (verbose)
			 printf(_("mount: %s already mounted on %s\n"),
				mc->m.mnt_fsname, mc->m.mnt_dir);
	       } else {
		    mtmp = (struct mntentchn *) xmalloc(sizeof(*mtmp));
		    *mtmp = *mc;
		    mtmp->nxt = 0;
		    g = NULL;
		    if (optfork) {
			 if (stat(mc->m.mnt_fsname, &statbuf) == 0 &&
			     S_ISBLK(statbuf.st_mode)) {
			      sprintf(major, "#%x", DISKMAJOR(statbuf.st_rdev));
			      g = major;
			 }
#ifdef HAVE_NFS
			 if (strcmp(mc->m.mnt_type, "nfs") == 0) {
			      g = xstrdup(mc->m.mnt_fsname);
			      colon = strchr(g, ':');
			      if (colon)
				   *colon = '\0';
			 }
#endif
		    }
		    if (g) {
			 for (cp = childhead.nxt; cp; cp = cp->nxt)
			      if (cp->group && strcmp(cp->group, g) == 0) {
				   cp->meclast->nxt = mtmp;
				   cp->meclast = mtmp;
				   goto fnd;
			      }
		    }
		    cp = (struct child *) xmalloc(sizeof *cp);
		    cp->nxt = 0;
		    cp->mec = cp->meclast = mtmp;
		    cp->group = xstrdup(g);
		    cp->pid = 0;
		    childtail->nxt = cp;
		    childtail = cp;
	       fnd:;
	       }
	  }
     }
			      
     /* now do everything */
     for (cp = childhead.nxt; cp; cp = cp->nxt) {
	  pid_t p = -1;
	  if (optfork) {
	       p = fork();
	       if (p == -1) {
	       	    int errsv = errno;
		    error(_("mount: cannot fork: %s"), strerror (errsv));
	       }
	       else if (p != 0)
		    cp->pid = p;
	  }

	  /* if child, or not forked, do the mounting */
	  if (p == 0 || p == -1) {
	       for (mc = cp->mec; mc; mc = mc->nxt)
		    status |= mount_one (mc->m.mnt_fsname, mc->m.mnt_dir,
					 mc->m.mnt_type, mc->m.mnt_opts,
					 options, 0, 0);
	       if (mountcount)
		    status |= EX_SOMEOK;
	       if (p == 0)
		    exit(status);
	  }
     }

     /* wait for children, if any */
     while ((cp = childhead.nxt) != NULL) {
	  childhead.nxt = cp->nxt;
	  if (cp->pid) {
	       int ret;
	  keep_waiting:
	       if(waitpid(cp->pid, &ret, 0) == -1) {
		    if (errno == EINTR)
			 goto keep_waiting;
		    perror("waitpid");
	       } else if (WIFEXITED(ret))
		    status |= WEXITSTATUS(ret);
	       else
		    status |= EX_SYSERR;
	  }
     }
     if (mountcount)
	  status |= EX_SOMEOK;
     return status;
}

extern char version[];
static struct option longopts[] = {
	{ "all", 0, 0, 'a' },
	{ "fake", 0, 0, 'f' },
	{ "fork", 0, 0, 'F' },
	{ "help", 0, 0, 'h' },
	{ "no-mtab", 0, 0, 'n' },
	{ "read-only", 0, 0, 'r' },
	{ "ro", 0, 0, 'r' },
	{ "verbose", 0, 0, 'v' },
	{ "version", 0, 0, 'V' },
	{ "read-write", 0, 0, 'w' },
	{ "rw", 0, 0, 'w' },
	{ "options", 1, 0, 'o' },
	{ "types", 1, 0, 't' },
	{ "bind", 0, 0, 128 },
	{ "replace", 0, 0, 129 },
	{ "after", 0, 0, 130 },
	{ "before", 0, 0, 131 },
	{ "over", 0, 0, 132 },
	{ NULL, 0, 0, 0 }
};

/* Keep the usage message at max 22 lines, each at most 70 chars long.
   The user should not need a pager to read it. */
static void
usage (FILE *fp, int n) {
	fprintf(fp, _(
	  "Usage: mount -V                 : print version\n"
	  "       mount -h                 : print this help\n"
	  "       mount                    : list mounted filesystems\n"
	  "       mount -l                 : idem, including volume labels\n"
	  "So far the informational part. Next the mounting.\n"
	  "The command is `mount [-t fstype] something somewhere'.\n"
	  "Details found in /etc/fstab may be omitted.\n"
	  "       mount -a                 : mount all stuff from /etc/fstab\n"
	  "       mount device             : mount device at the known place\n"
	  "       mount directory          : mount known device here\n"
	  "       mount -t type dev dir    : ordinary mount command\n"
	  "Note that one does not really mount a device, one mounts\n"
	  "a filesystem (of the given type) found on the device.\n"
	  "One can also mount an already visible directory tree elsewhere:\n"
	  "       mount --bind olddir newdir\n"
	  "A device can be given by name, say /dev/hda1 or /dev/cdrom,\n"
	  "or by label, using  -L label  or by uuid, using  -U uuid .\n"
	  "Union or stack mounts are specified using one of\n"
	  "       --replace, --after, --before, --over\n"
	  "Other options: [-nfFrsvw] [-o options].\n"
	  "For many more details, say  man 8 mount .\n"
	));
	unlock_mtab();
	exit (n);
}

int
main (int argc, char *argv[]) {
	int c, result = 0, specseen;
	char *options = NULL, *spec, *node;
	char *volumelabel = NULL;
	char *uuid = NULL;
	string_list types = NULL;
	struct mntentchn *mc;
	int fd;

	setlocale(LC_ALL, "");
	bindtextdomain(PACKAGE, LOCALEDIR);
	textdomain(PACKAGE);

	/* People report that a mount called from init without console
	   writes error messages to /etc/mtab
	   Let us try to avoid getting fd's 0,1,2 */
	while((fd = open("/dev/null", O_RDWR)) == 0 || fd == 1 || fd == 2) ;
	if (fd > 2)
		close(fd);

#ifdef DO_PS_FIDDLING
	initproctitle(argc, argv);
#endif

	while ((c = getopt_long (argc, argv, "afFhlL:no:rsU:vVwt:",
				 longopts, NULL)) != EOF) {
		switch (c) {
		case 'a':		/* mount everything in fstab */
			++all;
			break;
		case 'f':		/* fake: don't actually call mount(2) */
			++fake;
			break;
		case 'F':
			++optfork;
			break;
		case 'h':		/* help */
			usage (stdout, 0);
			break;
		case 'l':
			list_with_volumelabel = 1;
			break;
		case 'L':
			volumelabel = optarg;
			break;
		case 'n':		/* do not write /etc/mtab */
			++nomtab;
			break;
		case 'o':		/* specify mount options */
			if (options)
				options = xstrconcat3(options, ",", optarg);
			else
				options = xstrdup(optarg);
			break;
		case 'r':		/* mount readonly */
			readonly = 1;
			readwrite = 0;
			break;
		case 's':		/* allow sloppy mount options */
			sloppy = 1;
			break;
		case 't':		/* specify file system types */
			types = parse_list (optarg);
			break;
		case 'U':
			uuid = optarg;
			break;
		case 'v':		/* be chatty - more so if repeated */
			++verbose;
			break;
		case 'V':		/* version */
			printf ("mount: %s\n", version);
			exit (0);
		case 'w':		/* mount read/write */
			readwrite = 1;
			readonly = 0;
			break;
		case 0:
			break;

		case 128: /* bind */
			++bind;
			break;
		case 129: /* replace */
			mounttype = MS_REPLACE;
			break;
		case 130: /* after */
			mounttype = MS_AFTER;
			break;
		case 131: /* before */
			mounttype = MS_BEFORE;
			break;
		case 132: /* over */
			mounttype = MS_OVER;
			break;
	      
		case '?':
		default:
			usage (stderr, EX_USAGE);
		}
	}

	argc -= optind;
	argv += optind;

	specseen = (uuid || volumelabel) ? 1 : 0; 	/* yes, .. i know */

	if (argc+specseen == 0 && !all) {
		if (options || mounttype || bind)
			usage (stderr, EX_USAGE);
		return print_all (types);
	}

	if (getuid () != geteuid ()) {
		suid = 1;
		if (types || options || readwrite || nomtab || all || fake ||
		    bind || mounttype || (argc + specseen) != 1)
			die (EX_USAGE, _("mount: only root can do that"));
	}

	if (!nomtab && mtab_does_not_exist()) {
		if (verbose > 1)
			printf(_("mount: no %s found - creating it..\n"),
			       MOUNTED);
		create_mtab ();
	}

	if (specseen) {
		if (uuid)
			spec = get_spec_by_uuid(uuid);
		else
			spec = get_spec_by_volume_label(volumelabel);
		if (!spec)
			die (EX_USAGE, _("mount: no such partition found"));
		if (verbose)
			printf(_("mount: mounting %s\n"), spec);
	} else
		spec = NULL;		/* just for gcc */

	switch (argc+specseen) {
	case 0:
		/* mount -a */
		result = mount_all (types, options);
		if (result == 0 && verbose)
			error(_("not mounted anything"));
		break;

	case 1:
		/* mount [-nfrvw] [-o options] special | node */
		if (types != NULL)
			usage (stderr, EX_USAGE);
		if (specseen) {
			/* We know the device. Where shall we mount it? */
			mc = (uuid ? getfsuuidspec (uuid)
			           : getfsvolspec (volumelabel));
			if (mc == NULL)
				mc = getfsspec (spec);
			if (mc == NULL)
				die (EX_USAGE,
				     _("mount: cannot find %s in %s"),
				     spec, _PATH_FSTAB);
			mc->m.mnt_fsname = spec;
		} else {
			/* Try to find the other pathname in fstab.  */
			spec = canonicalize (*argv);
			if ((mc = getfsspec (spec)) == NULL &&
			    (mc = getfsfile (spec)) == NULL &&
			    /* Try noncanonical name in fstab
			       perhaps /dev/cdrom or /dos is a symlink */
			    (mc = getfsspec (*argv)) == NULL &&
			    (mc = getfsfile (*argv)) == NULL &&
			    /* Try mtab - maybe this was a remount */
			    (mc = getmntfile (spec)) == NULL)
				die (EX_USAGE,
				     _("mount: can't find %s in %s or %s"),
				     spec, _PATH_FSTAB, MOUNTED);
			/* Earlier mtab was tried first, but this would
			   sometimes try the wrong mount in case mtab had
			   the root device entry wrong. */
		}

		result = mount_one (xstrdup (mc->m.mnt_fsname),
				    xstrdup (mc->m.mnt_dir),
				    xstrdup (mc->m.mnt_type),
				    mc->m.mnt_opts, options, 0, 0);
		break;

	case 2:
		/* mount [-nfrvw] [-t vfstype] [-o options] special node */
		if (specseen) {
			/* we have spec already */
			node = argv[0];
		} else {
			spec = argv[0];
			node = argv[1];
		}
		if (types == NULL)
			result = mount_one (spec, node, NULL, NULL,
					    options, 0, 0);
		else if (cdr (types) == NULL)
			result = mount_one (spec, node, car (types), NULL,
					    options, 0, 0);
		else
			usage (stderr, EX_USAGE);
		break;
      
	default:
		usage (stderr, EX_USAGE);
	}

	if (result == EX_SOMEOK)
		result = 0;
	exit (result);
}