237 lines
		
	
	
	
		
			5.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			237 lines
		
	
	
	
		
			5.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* mkdir - make directories */
 | |
| 
 | |
| /* See Makefile for compilation details. */
 | |
| 
 | |
| /*
 | |
|    Copyright (C) 1999-2009 Free Software Foundation, Inc.
 | |
| 
 | |
|    This file is part of GNU Bash.
 | |
|    Bash is free software: you can redistribute it and/or modify
 | |
|    it under the terms of the GNU General Public License as published by
 | |
|    the Free Software Foundation, either version 3 of the License, or
 | |
|    (at your option) any later version.
 | |
| 
 | |
|    Bash is distributed in the hope that it will be useful,
 | |
|    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|    GNU General Public License for more details.
 | |
| 
 | |
|    You should have received a copy of the GNU General Public License
 | |
|    along with Bash.  If not, see <http://www.gnu.org/licenses/>.
 | |
| */
 | |
| 
 | |
| #include <config.h>
 | |
| 
 | |
| #include "bashtypes.h"
 | |
| #include "posixstat.h"
 | |
| #include <errno.h>
 | |
| #include <stdio.h>
 | |
| #include "bashansi.h"
 | |
| #if defined (HAVE_UNISTD_H)
 | |
| #  include <unistd.h>
 | |
| #endif
 | |
| 
 | |
| #include "builtins.h"
 | |
| #include "shell.h"
 | |
| #include "bashgetopt.h"
 | |
| #include "common.h"
 | |
| 
 | |
| #if !defined (errno)
 | |
| extern int errno;
 | |
| #endif
 | |
| 
 | |
| #define ISOCTAL(c)	((c) >= '0' && (c) <= '7')
 | |
| 
 | |
| extern int parse_symbolic_mode ();
 | |
| 
 | |
| static int make_path ();
 | |
| 
 | |
| static int original_umask;
 | |
| 
 | |
| int
 | |
| mkdir_builtin (list)
 | |
|      WORD_LIST *list;
 | |
| {
 | |
|   int opt, pflag, omode, rval, octal, nmode, parent_mode, um;
 | |
|   char *mode;
 | |
|   WORD_LIST *l;
 | |
| 
 | |
|   reset_internal_getopt ();
 | |
|   pflag = 0;
 | |
|   mode = (char *)NULL;
 | |
|   while ((opt = internal_getopt(list, "m:p")) != -1)
 | |
|     switch (opt)
 | |
|       {
 | |
| 	case 'p':
 | |
| 	  pflag = 1;
 | |
| 	  break;
 | |
| 	case 'm':
 | |
| 	  mode = list_optarg;
 | |
| 	  break;
 | |
| 	default:
 | |
| 	  builtin_usage();
 | |
| 	  return (EX_USAGE);
 | |
|       }
 | |
|   list = loptend;
 | |
| 
 | |
|   if (list == 0)
 | |
|     {
 | |
|       builtin_usage ();
 | |
|       return (EX_USAGE);
 | |
|     }
 | |
| 
 | |
|   if (mode == NULL)
 | |
|     omode = S_IRWXU | S_IRWXG | S_IRWXO;	/* a=rwx */
 | |
|   else if (ISOCTAL (*mode))	/* octal number */
 | |
|     {
 | |
|       omode = read_octal (mode);
 | |
|       if (omode < 0)
 | |
| 	{
 | |
| 	  builtin_error ("invalid file mode: %s", mode);
 | |
| 	  return (EXECUTION_FAILURE);
 | |
| 	}
 | |
|       octal = 1;
 | |
|     }
 | |
|   else if (mode)
 | |
|     {
 | |
|       /* initial bits are a=rwx; the mode argument modifies them */
 | |
|       omode = parse_symbolic_mode (mode, S_IRWXU | S_IRWXG | S_IRWXO);
 | |
|       if (omode < 0)
 | |
| 	{
 | |
| 	  builtin_error ("invalid file mode: %s", mode);
 | |
| 	  return (EXECUTION_FAILURE);
 | |
| 	}
 | |
|       octal = 0;
 | |
|     }
 | |
| 
 | |
|   /* Make the new mode */
 | |
|   original_umask = umask (0);
 | |
|   umask (original_umask);
 | |
| 
 | |
|   nmode = (S_IRWXU | S_IRWXG | S_IRWXO) & ~original_umask;
 | |
|   parent_mode = nmode | (S_IWRITE|S_IEXEC);	/* u+wx */
 | |
| 
 | |
|   /* Adjust new mode based on mode argument */
 | |
|   nmode &= omode;
 | |
| 
 | |
|   for (rval = EXECUTION_SUCCESS, l = list; l; l = l->next)
 | |
|     {
 | |
|       if (pflag && make_path (l->word->word, nmode, parent_mode))
 | |
| 	{
 | |
| 	  rval = EXECUTION_FAILURE;
 | |
| 	  continue;
 | |
| 	}
 | |
|       else if (pflag == 0 && mkdir (l->word->word, nmode) < 0)
 | |
|         {
 | |
|           builtin_error ("cannot create directory `%s': %s", l->word->word, strerror (errno));
 | |
|           rval = EXECUTION_FAILURE;
 | |
|         }
 | |
|     }
 | |
|   return rval;
 | |
| }
 | |
| 
 | |
| /* Make all the directories leading up to PATH, then create PATH.  Note that
 | |
|    this changes the process's umask; make sure that all paths leading to a
 | |
|    return reset it to ORIGINAL_UMASK */
 | |
| static int
 | |
| make_path (path, nmode, parent_mode)
 | |
|      char *path;
 | |
|      int nmode, parent_mode;
 | |
| {
 | |
|   int oumask;
 | |
|   struct stat sb;
 | |
|   char *p, *npath;
 | |
| 
 | |
|   if (stat (path, &sb) == 0)
 | |
|     {
 | |
|       if (S_ISDIR (sb.st_mode) == 0)
 | |
| 	{
 | |
| 	  builtin_error ("`%s': file exists but is not a directory", path);
 | |
| 	  return 1;
 | |
| 	}
 | |
| 	
 | |
|       if (chmod (path, nmode))
 | |
|         {
 | |
|           builtin_error ("%s: %s", path, strerror (errno));
 | |
|           return 1;
 | |
|         }
 | |
| 
 | |
|       return 0;
 | |
|     }
 | |
| 
 | |
|   oumask = umask (0);
 | |
|   npath = savestring (path);	/* So we can write to it. */
 | |
|     
 | |
|   /* Check whether or not we need to do anything with intermediate dirs. */
 | |
| 
 | |
|   /* Skip leading slashes. */
 | |
|   p = npath;
 | |
|   while (*p == '/')
 | |
|     p++;
 | |
| 
 | |
|   while (p = strchr (p, '/'))
 | |
|     {
 | |
|       *p = '\0';
 | |
|       if (stat (npath, &sb) != 0)
 | |
| 	{
 | |
| 	  if (mkdir (npath, parent_mode))
 | |
| 	    {
 | |
| 	      builtin_error ("cannot create directory `%s': %s", npath, strerror (errno));
 | |
| 	      umask (original_umask);
 | |
| 	      free (npath);
 | |
| 	      return 1;
 | |
| 	    }
 | |
| 	}
 | |
|       else if (S_ISDIR (sb.st_mode) == 0)
 | |
|         {
 | |
|           builtin_error ("`%s': file exists but is not a directory", npath);
 | |
|           umask (original_umask);
 | |
|           free (npath);
 | |
|           return 1;
 | |
|         }
 | |
| 
 | |
|       *p++ = '/';	/* restore slash */
 | |
|       while (*p == '/')
 | |
| 	p++;
 | |
|     }
 | |
| 
 | |
|   /* Create the final directory component. */
 | |
|   if (stat (npath, &sb) && mkdir (npath, nmode))
 | |
|     {
 | |
|       builtin_error ("cannot create directory `%s': %s", npath, strerror (errno));
 | |
|       umask (original_umask);
 | |
|       free (npath);
 | |
|       return 1;
 | |
|     }
 | |
| 
 | |
|   umask (original_umask);
 | |
|   free (npath);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| char *mkdir_doc[] = {
 | |
| 	"Create directories.",
 | |
| 	"",
 | |
| 	"Make directories.  Create the directories named as arguments, in",
 | |
| 	"the order specified, using mode rwxrwxrwx as modified by the current",
 | |
| 	"umask (see `help umask').  The -m option causes the file permission",
 | |
| 	"bits of the final directory to be MODE.  The MODE argument may be",
 | |
| 	"an octal number or a symbolic mode like that used by chmod(1).  If",
 | |
| 	"a symbolic mode is used, the operations are interpreted relative to",
 | |
| 	"an initial mode of \"a=rwx\".  The -p option causes any required",
 | |
| 	"intermediate directories in PATH to be created.  The directories",
 | |
| 	"are created with permssion bits of rwxrwxrwx as modified by the current",
 | |
| 	"umask, plus write and search permissions for the owner.  mkdir",
 | |
| 	"returns 0 if the directories are created successfully, and non-zero",
 | |
| 	"if an error occurs.",
 | |
| 	(char *)NULL
 | |
| };
 | |
| 
 | |
| struct builtin mkdir_struct = {
 | |
| 	"mkdir",
 | |
| 	mkdir_builtin,
 | |
| 	BUILTIN_ENABLED,
 | |
| 	mkdir_doc,
 | |
| 	"mkdir [-p] [-m mode] directory [directory ...]",
 | |
| 	0
 | |
| };
 | 
