Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F102958665
chmod.c
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Tue, Feb 25, 20:59
Size
21 KB
Mime Type
text/x-c
Expires
Thu, Feb 27, 20:59 (2 d)
Engine
blob
Format
Raw Data
Handle
24467709
Attached To
R3704 elastic-yarn
chmod.c
View Options
/**
* 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
);
}
Event Timeline
Log In to Comment