[GNU Manual] [POSIX requirement] [Linux man] [FreeBSD man]
Summary
chmod - change file mode bits
Lines of code: 573
Principal syscall: fchmodat()
Support syscalls: fstatat()
Options: 27 (17 short, 10 long, does not include perm digits)
Descended from chmod introduced in Version 1 UNIX (1971)
Added to Fileutils in October 1992 [First version]
Number of revisions: 174
describe_change()
- Converts change attempt results to human-readable textmode_changed()
- Boolean function to detect mode change usingfstatat()
process_file()
- Attempts mode change on a single fileprocess_files()
- Attempts mode change on a group of files
error()
- Outputs error message to standard error with possible process terminationget_root_dev_ino()
- Get's the dev and inode number of rootmode_adjust()
- Computes the new mode based on input changes (in Gnulib)mode_compile()
- Converts a string describing a mode to amode_t
mode_create_from_ref()
- Returns a mode from a reference filequoteaf()
- Converts an input file to a stringstrmode()
- Converts file mode from bit field to char arrayX2REALLOC()
- Macro for the same function to verify/create a memory area
Setup
At global scope, chmod.c does the following:
- Defines
Change_status
enum to identify the result of each file chmod - Defines
Verbosity
enum identifies levels of feedback - Defines pseudo-characters for long options without actual letters
- Initializes the long parsing options for getopt including: --changes, --recursive, --no-preserve-root, --preserve-root, --quiet, --reference, --silent, --verbose, --help, --version
It also traps all digits and capitals as short options with no arguments. The numbers are used to detect pids and the capitals force invalid argument handling during parse
main() initializes the following:
c
- The current command line option letter we're parsingdiagnose_surpises
- Flag to verify actual result is intended resultforce_silent
- Flag to prevent error feedbackmode
- The initial input mode string from the usermode_len
- The length of the mode stringmode_alloc
- The allocation size of the mode stringok
- The final return status. Note overloaded usagepreserve_root
- Flag to limit recursion from rootrecurse
- Flag to enforce recursion checksreference_file
- The file name used as a mode reference
Parsing kicks off with the short options passed as a string literal:
"Rcfvr::w::x::X::s::t::u::g::o::a::,::+::=::0::1::2::3::4::5::6::7::"
Parsing
During parsing, we're collecting options and arguments to answer the following questions:
- Which file(s) do we modify
- What is the new mode?
- Is the new mode provided directly or with a reference file
- Are we preserving root?
- Are we operating recursively
- What amount of reporting should we provide?
The expected input mode format follows convention: [ugo][+-][rwx] or in decimal triple (ex: 755). The parser creates a comma-separated sequence of the requested mode changes and 'compiles' them to the mode_t
type format expected by your system.
Parsing failures
These failure cases are explicitly checked:
- Specifying a mode but also providing a reference file
- Mismatch between actual and expected operand count
- Cannot access input reference file
- Cannot parse specified mode
- Cannot access filesystem root while preserving and recursing
User specified parsing failures result in a short error message followed by the usage instructions. Access related parsing errors die with an error message.
Execution
chmod
executes on all files sequentially and continues until all files are processed regardless of errors. The fchmod()
syscall applies mode changes while the fstatat()
syscall retrieves current file mode. There are four ways the change could occur:
- The change succeeds
- The mode already matches and no change is necessary
- There is nothing to change (a symbolic link)
- Something went wrong....
The last case is failure and could happen in several ways:
Failure cases:
- Insufficient permissions
- No file / bad link
- The resuling change isn't the expected change
All failures at this stage output an error message to STDERR and return without displaying usage help
Extra comments
File access and retrieval is performed with the fts library. There is a quirk that attempting to access a top-level file for the first time will require two attempts to succeed.
The ok
status variable is not global, but is reused within three nested scopes: main()
-> process_files()
, and process_file()
. Consequently, the final return value depends on the value returned of the last file processed or a failure of the fts system.
The chmodat()
used to change file modes is actually a wrapper for the real syscall. Here is where the wrapper leads to:
/* From Gnulib (openat.h) */ chmodat (int fd, char const *file, mode_t mode) { return fchmodat (fd, file, mode, 0); }