/*++ Copyright (c) 2013 Minoca Corp. All Rights Reserved Module Name: chmod.c Abstract: This module implements support for the chmod utility. Author: Evan Green 16-Jul-2013 Environment: POSIX --*/ // // ------------------------------------------------------------------- Includes // #include #include #include #include #include "swlib.h" // // ---------------------------------------------------------------- Definitions // // // Set this option to recursively change permissions for any directory. // #define CHMOD_OPTION_RECURSIVE 0x00000001 // // Set this option to print out a message for each file changed. // #define CHMOD_OPTION_VERBOSE 0x00000002 // // Set this option to suppress most error messages. // #define CHMOD_OPTION_QUIET 0x00000004 // // ------------------------------------------------------ Data Type Definitions // // // ----------------------------------------------- Internal Function Prototypes // INT ChmodChangePermissions ( ULONG Options, PSTR ModeString, PSTR Argument ); // // -------------------------------------------------------------------- Globals // // // ------------------------------------------------------------------ Functions // INT ChmodMain ( INT ArgumentCount, CHAR **Arguments ) /*++ Routine Description: This routine implements the main entry point for the chmod utility. Arguments: ArgumentCount - Supplies the number of arguments on the command line. Arguments - Supplies an array of pointers to strings representing the arguments. Return Value: 0 on success. Non-zero on failure. --*/ { PSTR Argument; ULONG ArgumentIndex; BOOL DidSomething; PSTR ModeString; ULONG Options; mode_t OriginalUmask; INT Result; INT ReturnValue; BOOL SkipControlArguments; ModeString = NULL; Options = 0; ReturnValue = 0; OriginalUmask = umask(0); // // Loop through all the options. // SkipControlArguments = TRUE; for (ArgumentIndex = 1; ArgumentIndex < ArgumentCount; ArgumentIndex += 1) { Argument = Arguments[ArgumentIndex]; if (SkipControlArguments != FALSE) { // // Stop if a -- is found. // if (strcmp(Argument, "--") == 0) { SkipControlArguments = FALSE; continue; } if (Argument[0] == '-') { // // Parse the argument, unless they're actually permissions. // if ((Argument[1] != 'r') && (Argument[1] != 'w') && (Argument[1] != 'x') && (Argument[1] != 's') && (Argument[1] != 'X') && (Argument[1] != 't') && (Argument[1] != 'u') && (Argument[1] != 'g') && (Argument[1] != 'o') && (Argument[1] != 'a')) { Argument += 1; while (*Argument != '\0') { switch (*Argument) { case 'R': Options |= CHMOD_OPTION_RECURSIVE; break; case 'v': Options |= CHMOD_OPTION_VERBOSE; Options &= ~CHMOD_OPTION_QUIET; break; case 'f': Options |= CHMOD_OPTION_QUIET; Options &= ~CHMOD_OPTION_VERBOSE; break; default: SwPrintError(0, NULL, "Unknown option %c", *Argument); Result = EINVAL; goto MainEnd; } Argument += 1; } continue; } } } // // This is a regular argument. // if (ModeString == NULL) { ModeString = Argument; } } if (ModeString == NULL) { SwPrintError(0, NULL, "Expecting mode argument"); ReturnValue = EINVAL; goto MainEnd; } // // Now that the options have been figured out, loop through again to // actually change permissions. // DidSomething = FALSE; SkipControlArguments = TRUE; for (ArgumentIndex = 1; ArgumentIndex < ArgumentCount; ArgumentIndex += 1) { Argument = Arguments[ArgumentIndex]; if (SkipControlArguments != FALSE) { // // If a -- is found, all other arguments are real parameters. // if (strcmp(Argument, "--") == 0) { SkipControlArguments = FALSE; continue; } if (Argument[0] == '-') { continue; } } if (Argument == ModeString) { continue; } DidSomething = TRUE; Result = ChmodChangePermissions(Options, ModeString, Argument); if (Result != 0) { ReturnValue = Result; } } if (DidSomething == FALSE) { SwPrintError(0, NULL, "Argument expected"); Result = 1; goto MainEnd; } MainEnd: umask(OriginalUmask); return ReturnValue; } // // --------------------------------------------------------- Internal Functions // INT ChmodChangePermissions ( ULONG Options, PSTR ModeString, PSTR Argument ) /*++ Routine Description: This routine changes the mode bits for the given file entry. Arguments: Options - Supplies the invocation options. See CHMOD_OPTION_* definitions. ModeString - Supplies the string of mode bits to change. Argument - Supplies the path of the file to change. Return Value: 0 on success. Non-zero on failure. --*/ { PSTR AppendedPath; ULONG AppendedPathSize; DIR *Directory; struct dirent Entry; struct dirent *EntryPointer; BOOL IsDirectory; mode_t NewMode; mode_t OriginalMode; CHAR PrintMode[10]; PSTR QuotedPath; INT Result; struct stat Stat; INT StringIndex; PSTR Verb; Directory = NULL; // // Get the file information. // Result = SwStat(Argument, FALSE, &Stat); if (Result != 0) { Result = errno; if ((Options & CHMOD_OPTION_QUIET) == 0) { SwPrintError(Result, Argument, "Cannot stat"); } goto ChangePermissionsEnd; } // // Skip symbolic links. // if (S_ISLNK(Stat.st_mode)) { if ((Options & CHMOD_OPTION_VERBOSE) != 0) { QuotedPath = SwQuoteArgument(Argument); printf("Neither symbolic link '%s' nor referent has been " "changed.\n", QuotedPath); if (QuotedPath != Argument) { free(QuotedPath); } } goto ChangePermissionsEnd; } OriginalMode = Stat.st_mode; NewMode = OriginalMode; IsDirectory = FALSE; if (S_ISDIR(Stat.st_mode)) { IsDirectory = TRUE; } Result = SwParseFilePermissionsString(ModeString, IsDirectory, &NewMode); if (Result == FALSE) { SwPrintError(0, ModeString, "Invalid mode"); Result = EINVAL; goto ChangePermissionsEnd; } // // Attempt to change the mode of this file or directory. // Result = chmod(Argument, NewMode); if (Result != 0) { Result = errno; if ((Options & CHMOD_OPTION_QUIET) == 0) { SwPrintError(Result, Argument, "Could not change mode of"); } goto ChangePermissionsEnd; } // // Print this out if verbose. // if ((Options & CHMOD_OPTION_VERBOSE) != 0) { for (StringIndex = 0; StringIndex < 9; StringIndex += 1) { PrintMode[StringIndex] = '-'; } if ((NewMode & S_IRUSR) != 0) { PrintMode[0] = 'r'; } if ((NewMode & S_IWUSR) != 0) { PrintMode[1] = 'w'; } if ((NewMode & S_IXUSR) != 0) { PrintMode[2] = 'x'; if ((NewMode & S_ISUID) != 0) { PrintMode[2] = 's'; } } else { if ((NewMode & S_ISUID) != 0) { PrintMode[2] = 'S'; } } if ((NewMode & S_IRGRP) != 0) { PrintMode[3] = 'r'; } if ((NewMode & S_IWGRP) != 0) { PrintMode[4] = 'w'; } if ((NewMode & S_IXGRP) != 0) { PrintMode[5] = 'x'; if ((NewMode & S_ISGID) != 0) { PrintMode[5] = 's'; } } else { if ((NewMode & S_ISGID) != 0) { PrintMode[5] = 'S'; } } if ((NewMode & S_IROTH) != 0) { PrintMode[6] = 'r'; } if ((NewMode & S_IWOTH) != 0) { PrintMode[7] = 'w'; } if ((NewMode & S_IXOTH) != 0) { PrintMode[8] = 'x'; if ((NewMode & S_ISVTX) != 0) { PrintMode[8] = 't'; } } else { if ((NewMode & S_ISVTX) != 0) { PrintMode[8] = 'T'; } } PrintMode[9] = '\0'; if (NewMode != OriginalMode) { Verb = "changed to"; } else { Verb = "retained as"; } QuotedPath = SwQuoteArgument(Argument); printf("mode of '%s' %s 0%03o (%s)\n", QuotedPath, Verb, NewMode & (S_IRWXU | S_IRWXG | S_IRWXO), PrintMode); if (QuotedPath != Argument) { free(QuotedPath); } } // // If the options are not recursive or this is not a directory, then that's // all there is to do. // if (((Options & CHMOD_OPTION_RECURSIVE) == 0) || (IsDirectory == FALSE)) { goto ChangePermissionsEnd; } Directory = opendir(Argument); if (Directory == NULL) { Result = errno; if ((Options & CHMOD_OPTION_QUIET) == 0) { SwPrintError(Result, Argument, "Cannot open directory"); } goto ChangePermissionsEnd; } // // Loop through all entries in the directory. // while (TRUE) { Result = SwReadDirectory(Directory, &Entry, &EntryPointer); if (Result != 0) { if ((Options & CHMOD_OPTION_QUIET) == 0) { SwPrintError(Result, Argument, "Unable to read directory"); } goto ChangePermissionsEnd; } if (EntryPointer == NULL) { break; } if ((strcmp(Entry.d_name, ".") == 0) || (strcmp(Entry.d_name, "..") == 0)) { continue; } Result = SwAppendPath(Argument, strlen(Argument) + 1, Entry.d_name, strlen(Entry.d_name) + 1, &AppendedPath, &AppendedPathSize); if (Result == FALSE) { Result = ENOMEM; goto ChangePermissionsEnd; } Result = ChmodChangePermissions(Options, ModeString, AppendedPath); free(AppendedPath); if (Result != 0) { goto ChangePermissionsEnd; } } ChangePermissionsEnd: if (Directory != NULL) { closedir(Directory); } return Result; }