/hadoop-common-project/hadoop-common/src/main/winutils/chmod.c
C | 892 lines | 783 code | 38 blank | 71 comment | 29 complexity | 2efe7b5fb11ea5211ecacb6ed4bb65f4 MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause
- /**
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
- #include "winutils.h"
- #include <errno.h>
- enum CHMOD_WHO
- {
- CHMOD_WHO_NONE = 0,
- CHMOD_WHO_OTHER = 07,
- CHMOD_WHO_GROUP = 070,
- CHMOD_WHO_USER = 0700,
- CHMOD_WHO_ALL = CHMOD_WHO_OTHER | CHMOD_WHO_GROUP | CHMOD_WHO_USER
- };
- enum CHMOD_OP
- {
- CHMOD_OP_INVALID,
- CHMOD_OP_PLUS,
- CHMOD_OP_MINUS,
- CHMOD_OP_EQUAL,
- };
- enum CHMOD_PERM
- {
- CHMOD_PERM_NA = 00,
- CHMOD_PERM_R = 01,
- CHMOD_PERM_W = 02,
- CHMOD_PERM_X = 04,
- CHMOD_PERM_LX = 010,
- };
- /*
- * We use the following struct to build a linked list of mode change actions.
- * The mode is described by the following grammar:
- * mode ::= clause [, clause ...]
- * clause ::= [who ...] [action ...]
- * action ::= op [perm ...] | op [ref]
- * who ::= a | u | g | o
- * op ::= + | - | =
- * perm ::= r | w | x | X
- * ref ::= u | g | o
- */
- typedef struct _MODE_CHANGE_ACTION
- {
- USHORT who;
- USHORT op;
- USHORT perm;
- USHORT ref;
- struct _MODE_CHANGE_ACTION *next_action;
- } MODE_CHANGE_ACTION, *PMODE_CHANGE_ACTION;
- const MODE_CHANGE_ACTION INIT_MODE_CHANGE_ACTION = {
- CHMOD_WHO_NONE, CHMOD_OP_INVALID, CHMOD_PERM_NA, CHMOD_WHO_NONE, NULL
- };
- static BOOL ParseOctalMode(LPCWSTR tsMask, INT *uMask);
- static BOOL ParseMode(LPCWSTR modeString, PMODE_CHANGE_ACTION *actions);
- static BOOL FreeActions(PMODE_CHANGE_ACTION actions);
- static BOOL ParseCommandLineArguments(
- __in int argc,
- __in_ecount(argc) wchar_t *argv[],
- __out BOOL *rec,
- __out_opt INT *mask,
- __out_opt PMODE_CHANGE_ACTION *actions,
- __out LPCWSTR *path);
- static BOOL ChangeFileModeByActions(__in LPCWSTR path,
- MODE_CHANGE_ACTION const *actions);
- static BOOL ChangeFileMode(__in LPCWSTR path, __in_opt INT mode,
- __in_opt MODE_CHANGE_ACTION const *actions);
- static BOOL ChangeFileModeRecursively(__in LPCWSTR path, __in_opt INT mode,
- __in_opt MODE_CHANGE_ACTION const *actions);
- //----------------------------------------------------------------------------
- // Function: Chmod
- //
- // Description:
- // The main method for chmod command
- //
- // Returns:
- // 0: on success
- //
- // Notes:
- //
- int Chmod(__in int argc, __in_ecount(argc) wchar_t *argv[])
- {
- LPWSTR pathName = NULL;
- LPWSTR longPathName = NULL;
- BOOL recursive = FALSE;
- PMODE_CHANGE_ACTION actions = NULL;
- INT unixAccessMask = 0;
- DWORD dwRtnCode = 0;
- int ret = EXIT_FAILURE;
- // Parsing chmod arguments
- //
- if (!ParseCommandLineArguments(argc, argv,
- &recursive, &unixAccessMask, &actions, &pathName))
- {
- fwprintf(stderr, L"Incorrect command line arguments.\n\n");
- ChmodUsage(argv[0]);
- return EXIT_FAILURE;
- }
- // Convert the path the the long path
- //
- dwRtnCode = ConvertToLongPath(pathName, &longPathName);
- if (dwRtnCode != ERROR_SUCCESS)
- {
- ReportErrorCode(L"ConvertToLongPath", dwRtnCode);
- goto ChmodEnd;
- }
- if (!recursive)
- {
- if (ChangeFileMode(longPathName, unixAccessMask, actions))
- {
- ret = EXIT_SUCCESS;
- }
- }
- else
- {
- if (ChangeFileModeRecursively(longPathName, unixAccessMask, actions))
- {
- ret = EXIT_SUCCESS;
- }
- }
- ChmodEnd:
- FreeActions(actions);
- LocalFree(longPathName);
- return ret;
- }
- //----------------------------------------------------------------------------
- // Function: ChangeFileMode
- //
- // Description:
- // Wrapper function for change file mode. Choose either change by action or by
- // access mask.
- //
- // Returns:
- // TRUE: on success
- // FALSE: otherwise
- //
- // Notes:
- //
- static BOOL ChangeFileMode(__in LPCWSTR path, __in_opt INT unixAccessMask,
- __in_opt MODE_CHANGE_ACTION const *actions)
- {
- if (actions != NULL)
- return ChangeFileModeByActions(path, actions);
- else
- {
- DWORD dwRtnCode = ChangeFileModeByMask(path, unixAccessMask);
- if (dwRtnCode != ERROR_SUCCESS)
- {
- ReportErrorCode(L"ChangeFileModeByMask", dwRtnCode);
- return FALSE;
- }
- return TRUE;
- }
- }
- //----------------------------------------------------------------------------
- // Function: ChangeFileModeRecursively
- //
- // Description:
- // Travel the directory recursively to change the permissions.
- //
- // Returns:
- // TRUE: on success
- // FALSE: otherwise
- //
- // Notes:
- // The recursion works in the following way:
- // - If the path is not a directory, change its mode and return.
- // Symbolic links and junction points are not considered as directories.
- // - Otherwise, call the method on all its children, then change its mode.
- //
- static BOOL ChangeFileModeRecursively(__in LPCWSTR path, __in_opt INT mode,
- __in_opt MODE_CHANGE_ACTION const *actions)
- {
- BOOL isDir = FALSE;
- BOOL isSymlink = FALSE;
- LPWSTR dir = NULL;
- size_t pathSize = 0;
- size_t dirSize = 0;
- HANDLE hFind = INVALID_HANDLE_VALUE;
- WIN32_FIND_DATA ffd;
- DWORD dwRtnCode = ERROR_SUCCESS;
- BOOL ret = FALSE;
- if ((dwRtnCode = DirectoryCheck(path, &isDir)) != ERROR_SUCCESS)
- {
- ReportErrorCode(L"IsDirectory", dwRtnCode);
- return FALSE;
- }
- if ((dwRtnCode = SymbolicLinkCheck(path, &isSymlink)) != ERROR_SUCCESS)
- {
- ReportErrorCode(L"IsSymbolicLink", dwRtnCode);
- return FALSE;
- }
- if (isSymlink || !isDir)
- {
- if (ChangeFileMode(path, mode, actions))
- return TRUE;
- else
- return FALSE;
- }
- if (FAILED(StringCchLengthW(path, STRSAFE_MAX_CCH - 3, &pathSize)))
- {
- return FALSE;
- }
- dirSize = pathSize + 3;
- dir = (LPWSTR)LocalAlloc(LPTR, dirSize * sizeof(WCHAR));
- if (dir == NULL)
- {
- ReportErrorCode(L"LocalAlloc", GetLastError());
- goto ChangeFileModeRecursivelyEnd;
- }
- if (FAILED(StringCchCopyW(dir, dirSize, path)) ||
- FAILED(StringCchCatW(dir, dirSize, L"\\*")))
- {
- goto ChangeFileModeRecursivelyEnd;
- }
- hFind = FindFirstFile(dir, &ffd);
- if (hFind == INVALID_HANDLE_VALUE)
- {
- ReportErrorCode(L"FindFirstFile", GetLastError());
- goto ChangeFileModeRecursivelyEnd;
- }
- do
- {
- LPWSTR filename = NULL;
- LPWSTR longFilename = NULL;
- size_t filenameSize = 0;
- if (wcscmp(ffd.cFileName, L".") == 0 ||
- wcscmp(ffd.cFileName, L"..") == 0)
- continue;
- filenameSize = pathSize + wcslen(ffd.cFileName) + 2;
- filename = (LPWSTR)LocalAlloc(LPTR, filenameSize * sizeof(WCHAR));
- if (filename == NULL)
- {
- ReportErrorCode(L"LocalAlloc", GetLastError());
- goto ChangeFileModeRecursivelyEnd;
- }
- if (FAILED(StringCchCopyW(filename, filenameSize, path)) ||
- FAILED(StringCchCatW(filename, filenameSize, L"\\")) ||
- FAILED(StringCchCatW(filename, filenameSize, ffd.cFileName)))
- {
- LocalFree(filename);
- goto ChangeFileModeRecursivelyEnd;
- }
-
- // The child fileanme is not prepended with long path prefix.
- // Convert the filename to long path format.
- //
- dwRtnCode = ConvertToLongPath(filename, &longFilename);
- LocalFree(filename);
- if (dwRtnCode != ERROR_SUCCESS)
- {
- ReportErrorCode(L"ConvertToLongPath", dwRtnCode);
- LocalFree(longFilename);
- goto ChangeFileModeRecursivelyEnd;
- }
- if(!ChangeFileModeRecursively(longFilename, mode, actions))
- {
- LocalFree(longFilename);
- goto ChangeFileModeRecursivelyEnd;
- }
- LocalFree(longFilename);
- } while (FindNextFileW(hFind, &ffd));
- if (!ChangeFileMode(path, mode, actions))
- {
- goto ChangeFileModeRecursivelyEnd;
- }
- ret = TRUE;
- ChangeFileModeRecursivelyEnd:
- LocalFree(dir);
- return ret;
- }
- //----------------------------------------------------------------------------
- // Function: ParseCommandLineArguments
- //
- // Description:
- // Parse command line arguments for chmod.
- //
- // Returns:
- // TRUE: on success
- // FALSE: otherwise
- //
- // Notes:
- // 1. Recursive is only set on directories
- // 2. 'actions' is NULL if the mode is octal
- //
- static BOOL ParseCommandLineArguments(
- __in int argc,
- __in_ecount(argc) wchar_t *argv[],
- __out BOOL *rec,
- __out_opt INT *mask,
- __out_opt PMODE_CHANGE_ACTION *actions,
- __out LPCWSTR *path)
- {
- LPCWSTR maskString;
- BY_HANDLE_FILE_INFORMATION fileInfo;
- DWORD dwRtnCode = ERROR_SUCCESS;
- assert(path != NULL);
- if (argc != 3 && argc != 4)
- return FALSE;
- *rec = FALSE;
- if (argc == 4)
- {
- maskString = argv[2];
- *path = argv[3];
- if (wcscmp(argv[1], L"-R") == 0)
- {
- // Check if the given path name is a file or directory
- // Only set recursive flag if the given path is a directory
- //
- dwRtnCode = GetFileInformationByName(*path, FALSE, &fileInfo);
- if (dwRtnCode != ERROR_SUCCESS)
- {
- ReportErrorCode(L"GetFileInformationByName", dwRtnCode);
- return FALSE;
- }
- if (IsDirFileInfo(&fileInfo))
- {
- *rec = TRUE;
- }
- }
- else
- return FALSE;
- }
- else
- {
- maskString = argv[1];
- *path = argv[2];
- }
- if (ParseOctalMode(maskString, mask))
- {
- return TRUE;
- }
- else if (ParseMode(maskString, actions))
- {
- return TRUE;
- }
- return FALSE;
- }
- //----------------------------------------------------------------------------
- // Function: FreeActions
- //
- // Description:
- // Free a linked list of mode change actions given the head node.
- //
- // Returns:
- // TRUE: on success
- // FALSE: otherwise
- //
- // Notes:
- // none
- //
- static BOOL FreeActions(PMODE_CHANGE_ACTION actions)
- {
- PMODE_CHANGE_ACTION curr = NULL;
- PMODE_CHANGE_ACTION next = NULL;
-
- // Nothing to free if NULL is passed in
- //
- if (actions == NULL)
- {
- return TRUE;
- }
- curr = actions;
- while (curr != NULL)
- {
- next = curr->next_action;
- LocalFree(curr);
- curr = next;
- }
- actions = NULL;
- return TRUE;
- }
- //----------------------------------------------------------------------------
- // Function: ComputeNewMode
- //
- // Description:
- // Compute a new mode based on the old mode and a mode change action.
- //
- // Returns:
- // The newly computed mode
- //
- // Notes:
- // Apply 'rwx' permission mask or reference permission mode according to the
- // '+', '-', or '=' operator.
- //
- static INT ComputeNewMode(__in INT oldMode,
- __in USHORT who, __in USHORT op,
- __in USHORT perm, __in USHORT ref)
- {
- static const INT readMask = 0444;
- static const INT writeMask = 0222;
- static const INT exeMask = 0111;
- INT mask = 0;
- INT mode = 0;
- // Operations are exclusive, and cannot be invalid
- //
- assert(op == CHMOD_OP_EQUAL || op == CHMOD_OP_PLUS || op == CHMOD_OP_MINUS);
- // Nothing needs to be changed if there is not permission or reference
- //
- if(perm == CHMOD_PERM_NA && ref == CHMOD_WHO_NONE)
- {
- return oldMode;
- }
- // We should have only permissions or a reference target, not both.
- //
- assert((perm != CHMOD_PERM_NA && ref == CHMOD_WHO_NONE) ||
- (perm == CHMOD_PERM_NA && ref != CHMOD_WHO_NONE));
- if (perm != CHMOD_PERM_NA)
- {
- if ((perm & CHMOD_PERM_R) == CHMOD_PERM_R)
- mask |= readMask;
- if ((perm & CHMOD_PERM_W) == CHMOD_PERM_W)
- mask |= writeMask;
- if ((perm & CHMOD_PERM_X) == CHMOD_PERM_X)
- mask |= exeMask;
- if (((perm & CHMOD_PERM_LX) == CHMOD_PERM_LX))
- {
- // It applies execute permissions to directories regardless of their
- // current permissions and applies execute permissions to a file which
- // already has at least 1 execute permission bit already set (either user,
- // group or other). It is only really useful when used with '+' and
- // usually in combination with the -R option for giving group or other
- // access to a big directory tree without setting execute permission on
- // normal files (such as text files), which would normally happen if you
- // just used "chmod -R a+rx .", whereas with 'X' you can do
- // "chmod -R a+rX ." instead (Source: Wikipedia)
- //
- if ((oldMode & UX_DIRECTORY) == UX_DIRECTORY || (oldMode & exeMask))
- mask |= exeMask;
- }
- }
- else if (ref != CHMOD_WHO_NONE)
- {
- mask |= oldMode & ref;
- switch(ref)
- {
- case CHMOD_WHO_GROUP:
- mask |= mask >> 3;
- mask |= mask << 3;
- break;
- case CHMOD_WHO_OTHER:
- mask |= mask << 3;
- mask |= mask << 6;
- break;
- case CHMOD_WHO_USER:
- mask |= mask >> 3;
- mask |= mask >> 6;
- break;
- default:
- // Reference modes can only be U/G/O and are exclusive
- assert(FALSE);
- }
- }
- mask &= who;
- if (op == CHMOD_OP_EQUAL)
- {
- mode = (oldMode & (~who)) | mask;
- }
- else if (op == CHMOD_OP_MINUS)
- {
- mode = oldMode & (~mask);
- }
- else if (op == CHMOD_OP_PLUS)
- {
- mode = oldMode | mask;
- }
- return mode;
- }
- //----------------------------------------------------------------------------
- // Function: ConvertActionsToMask
- //
- // Description:
- // Convert a linked list of mode change actions to the Unix permission mask
- // given the head node.
- //
- // Returns:
- // TRUE: on success
- // FALSE: otherwise
- //
- // Notes:
- // none
- //
- static BOOL ConvertActionsToMask(__in LPCWSTR path,
- __in MODE_CHANGE_ACTION const *actions, __out PINT puMask)
- {
- MODE_CHANGE_ACTION const *curr = NULL;
- DWORD dwErrorCode = ERROR_SUCCESS;
- INT mode = 0;
- dwErrorCode = FindFileOwnerAndPermission(path, FALSE, NULL, NULL, &mode);
- if (dwErrorCode != ERROR_SUCCESS)
- {
- ReportErrorCode(L"FindFileOwnerAndPermission", dwErrorCode);
- return FALSE;
- }
- *puMask = mode;
- // Nothing to change if NULL is passed in
- //
- if (actions == NULL)
- {
- return TRUE;
- }
- for (curr = actions; curr != NULL; curr = curr->next_action)
- {
- mode = ComputeNewMode(mode, curr->who, curr->op, curr->perm, curr->ref);
- }
- *puMask = mode;
- return TRUE;
- }
- //----------------------------------------------------------------------------
- // Function: ChangeFileModeByActions
- //
- // Description:
- // Change a file mode through a list of actions.
- //
- // Returns:
- // TRUE: on success
- // FALSE: otherwise
- //
- // Notes:
- // none
- //
- static BOOL ChangeFileModeByActions(__in LPCWSTR path,
- MODE_CHANGE_ACTION const *actions)
- {
- INT mask = 0;
- if (ConvertActionsToMask(path, actions, &mask))
- {
- DWORD dwRtnCode = ChangeFileModeByMask(path, mask);
- if (dwRtnCode != ERROR_SUCCESS)
- {
- ReportErrorCode(L"ChangeFileModeByMask", dwRtnCode);
- return FALSE;
- }
- return TRUE;
- }
- else
- return FALSE;
- }
- //----------------------------------------------------------------------------
- // Function: ParseMode
- //
- // Description:
- // Convert a mode string into a linked list of actions
- //
- // Returns:
- // TRUE: on success
- // FALSE: otherwise
- //
- // Notes:
- // Take a state machine approach to parse the mode. Each mode change action
- // will be a node in the output linked list. The state machine has five state,
- // and each will only transit to the next; the end state can transit back to
- // the first state, and thus form a circle. In each state, if we see a
- // a character not belongs to the state, we will move to next state. WHO, PERM,
- // and REF states are optional; OP and END states are required; and errors
- // will only be reported at the latter two states.
- //
- static BOOL ParseMode(LPCWSTR modeString, PMODE_CHANGE_ACTION *pActions)
- {
- enum __PARSE_MODE_ACTION_STATE
- {
- PARSE_MODE_ACTION_WHO_STATE,
- PARSE_MODE_ACTION_OP_STATE,
- PARSE_MODE_ACTION_PERM_STATE,
- PARSE_MODE_ACTION_REF_STATE,
- PARSE_MODE_ACTION_END_STATE
- } state = PARSE_MODE_ACTION_WHO_STATE;
- MODE_CHANGE_ACTION action = INIT_MODE_CHANGE_ACTION;
- PMODE_CHANGE_ACTION actionsEnd = NULL;
- PMODE_CHANGE_ACTION actionsLast = NULL;
- USHORT lastWho;
- WCHAR c = 0;
- size_t len = 0;
- size_t i = 0;
- assert(modeString != NULL && pActions != NULL);
- if (FAILED(StringCchLengthW(modeString, STRSAFE_MAX_CCH, &len)))
- {
- return FALSE;
- }
- actionsEnd = *pActions;
- while(i <= len)
- {
- c = modeString[i];
- if (state == PARSE_MODE_ACTION_WHO_STATE)
- {
- switch (c)
- {
- case L'a':
- action.who |= CHMOD_WHO_ALL;
- i++;
- break;
- case L'u':
- action.who |= CHMOD_WHO_USER;
- i++;
- break;
- case L'g':
- action.who |= CHMOD_WHO_GROUP;
- i++;
- break;
- case L'o':
- action.who |= CHMOD_WHO_OTHER;
- i++;
- break;
- default:
- state = PARSE_MODE_ACTION_OP_STATE;
- } // WHO switch
- }
- else if (state == PARSE_MODE_ACTION_OP_STATE)
- {
- switch (c)
- {
- case L'+':
- action.op = CHMOD_OP_PLUS;
- break;
- case L'-':
- action.op = CHMOD_OP_MINUS;
- break;
- case L'=':
- action.op = CHMOD_OP_EQUAL;
- break;
- default:
- fwprintf(stderr, L"Invalid mode: '%s'\n", modeString);
- FreeActions(*pActions);
- return FALSE;
- } // OP switch
- i++;
- state = PARSE_MODE_ACTION_PERM_STATE;
- }
- else if (state == PARSE_MODE_ACTION_PERM_STATE)
- {
- switch (c)
- {
- case L'r':
- action.perm |= CHMOD_PERM_R;
- i++;
- break;
- case L'w':
- action.perm |= CHMOD_PERM_W;
- i++;
- break;
- case L'x':
- action.perm |= CHMOD_PERM_X;
- i++;
- break;
- case L'X':
- action.perm |= CHMOD_PERM_LX;
- i++;
- break;
- default:
- state = PARSE_MODE_ACTION_REF_STATE;
- } // PERM switch
- }
- else if (state == PARSE_MODE_ACTION_REF_STATE)
- {
- switch (c)
- {
- case L'u':
- action.ref = CHMOD_WHO_USER;
- i++;
- break;
- case L'g':
- action.ref = CHMOD_WHO_GROUP;
- i++;
- break;
- case L'o':
- action.ref = CHMOD_WHO_OTHER;
- i++;
- break;
- default:
- state = PARSE_MODE_ACTION_END_STATE;
- } // REF switch
- }
- else if (state == PARSE_MODE_ACTION_END_STATE)
- {
- switch (c)
- {
- case NULL:
- __fallthrough;
- case L',':
- i++;
- __fallthrough;
- case L'+':
- __fallthrough;
- case L'-':
- __fallthrough;
- case L'=':
- state = PARSE_MODE_ACTION_WHO_STATE;
- // Append the current action to the end of the linked list
- //
- assert(actionsEnd == NULL);
- // Allocate memory
- actionsEnd = (PMODE_CHANGE_ACTION) LocalAlloc(LPTR,
- sizeof(MODE_CHANGE_ACTION));
- if (actionsEnd == NULL)
- {
- ReportErrorCode(L"LocalAlloc", GetLastError());
- FreeActions(*pActions);
- return FALSE;
- }
- if (action.who == CHMOD_WHO_NONE) action.who = CHMOD_WHO_ALL;
- // Copy the action to the new node
- *actionsEnd = action;
- // Append to the last node in the linked list
- if (actionsLast != NULL) actionsLast->next_action = actionsEnd;
- // pActions should point to the head of the linked list
- if (*pActions == NULL) *pActions = actionsEnd;
- // Update the two pointers to point to the last node and the tail
- actionsLast = actionsEnd;
- actionsEnd = actionsLast->next_action;
- // Reset action
- //
- lastWho = action.who;
- action = INIT_MODE_CHANGE_ACTION;
- if (c != L',')
- {
- action.who = lastWho;
- }
- break;
- default:
- fwprintf(stderr, L"Invalid mode: '%s'\n", modeString);
- FreeActions(*pActions);
- return FALSE;
- } // END switch
- }
- } // while
- return TRUE;
- }
- //----------------------------------------------------------------------------
- // Function: ParseOctalMode
- //
- // Description:
- // Convert the 3 or 4 digits Unix mask string into the binary representation
- // of the Unix access mask, i.e. 9 bits each an indicator of the permission
- // of 'rwxrwxrwx', i.e. user's, group's, and owner's read, write, and
- // execute/search permissions.
- //
- // Returns:
- // TRUE: on success
- // FALSE: otherwise
- //
- // Notes:
- // none
- //
- static BOOL ParseOctalMode(LPCWSTR tsMask, INT *uMask)
- {
- size_t tsMaskLen = 0;
- DWORD i;
- LONG l;
- WCHAR *end;
- if (uMask == NULL)
- return FALSE;
- if (FAILED(StringCchLengthW(tsMask, STRSAFE_MAX_CCH, &tsMaskLen)))
- return FALSE;
- if (tsMaskLen == 0 || tsMaskLen > 4)
- {
- return FALSE;
- }
- for (i = 0; i < tsMaskLen; i++)
- {
- if (!(tsMask[tsMaskLen - i - 1] >= L'0' &&
- tsMask[tsMaskLen - i - 1] <= L'7'))
- return FALSE;
- }
- errno = 0;
- if (tsMaskLen == 4)
- // Windows does not have any equivalent of setuid/setgid and sticky bit.
- // So the first bit is omitted for the 4 digit octal mode case.
- //
- l = wcstol(tsMask + 1, &end, 8);
- else
- l = wcstol(tsMask, &end, 8);
- if (errno || l > 0x0777 || l < 0 || *end != 0)
- {
- return FALSE;
- }
- *uMask = (INT) l;
- return TRUE;
- }
- void ChmodUsage(LPCWSTR program)
- {
- fwprintf(stdout, L"\
- Usage: %s [OPTION] OCTAL-MODE [FILE]\n\
- or: %s [OPTION] MODE [FILE]\n\
- Change the mode of the FILE to MODE.\n\
- \n\
- -R: change files and directories recursively\n\
- \n\
- Each MODE is of the form '[ugoa]*([-+=]([rwxX]*|[ugo]))+'.\n",
- program, program);
- }