| #!/bin/sh |
| |
| # Copyright 2015-2023 Rivoreo |
| |
| # Permission is hereby granted, free of charge, to any person obtaining a copy |
| # of this software and associated documentation files (the "Software"), to |
| # deal in the Software without restriction, including without limitation the |
| # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
| # sell copies of the Software, and to permit persons to whom the Software is |
| # furnished to do so, subject to the following conditions: |
| |
| # The above copyright notice and this permission notice shall be included in |
| # all copies or substantial portions of the Software. |
| |
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
| # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR |
| # IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| # SOFTWARE. |
| |
| |
| PROGRAM_DIR="`dirname \"$0\"`" |
| . "$PROGRAM_DIR/chflags.common.sh" |
| |
| case "`uname -sm`" in |
| "Linux mips"*|"Linux ppc"*|"Linux sparc"*) |
| EXT2_IOC_GETFLAGS=0x40086601 |
| EXT2_IOC_SETFLAGS=0x80086602 |
| ;; |
| Linux\ *) |
| EXT2_IOC_GETFLAGS=0x80086601 |
| EXT2_IOC_SETFLAGS=0x40086602 |
| ;; |
| #*FreeBSD\ *) |
| # EXT2_IOC_GETFLAGS=0x40086601 |
| # EXT2_IOC_SETFLAGS=0x80086602 |
| # ;; |
| *) |
| echo "Kernel type not supported" 1>&2 |
| exit 1 |
| esac |
| |
| EXT2_IMMUTABLE_FL=0x00000010 |
| EXT2_APPEND_FL=0x00000020 |
| |
| . "$PROGRAM_DIR/which.sh" |
| |
| if ioctl_program_path="`which toolbox`"; then |
| get_flags() { |
| local v |
| v="`toolbox ioctl -l $flags_size -r -- \"$1\" $EXT2_IOC_GETFLAGS`" || return |
| v="${v#* |
| }" |
| flags= |
| for b in ${v#*:}; do |
| b=${b#0x} |
| [ "$flags_order" = big ] && flags=$flags$b || flags=$b$flags |
| done |
| flags=0x$flags |
| } |
| set_flags() { |
| toolbox ioctl -l $flags_size -r -- "$1" $EXT2_IOC_SETFLAGS $2 > /dev/null |
| } |
| elif ioctl_program_path="`get_perl_executable_path`"; then |
| # Test function, unused |
| ioctl() { |
| local length=4 arg_length=4 arg_type |
| OPTIND=0 |
| while getopts rl:a:s c |
| do case $c in |
| r) |
| ;; |
| l) |
| length="$OPTARG" |
| ;; |
| a) |
| arg_length="$OPTARG" |
| #arg_type="C$((OPTARG))" |
| ;; |
| s) |
| arg_type=a |
| ;; |
| \?) |
| return 255 |
| ;; |
| esac done |
| if [ $# -le $OPTIND ]; then |
| echo "Usage: ioctl [<options>] <file> <request> [<arg> ...]" 1>&2 |
| return 255 |
| fi |
| shift $((OPTIND-1)) |
| local f="$1" req="$2" |
| shift 2 |
| local total_arg_len=$((arg_length*$#)) |
| [ $((length)) -lt $total_arg_len ] && length=$total_arg_len |
| [ "$arg_type" != a ] && arg_type="C$((length))" |
| perl -e 'my $buffer; $buffer = pack(@ARGV[2], splice(@ARGV, 3)) if $#ARGV > 2; my $res; open(HANDLE, "<@ARGV[0]") && ($res = ioctl(HANDLE, @ARGV[1], $buffer)) || die "@ARGV[0]: $!\n"; close(HANDLE); $res =~ s/ .+//; print STDOUT "$res\n"; my @r_buffer = unpack(@ARGV[2], $buffer); exit unless @r_buffer; print STDOUT "return:"; foreach $a (@r_buffer) { print STDOUT sprintf(" 0x%02x", $a); } print "\n";' -- "$f" $((req)) $arg_type "$@" |
| } |
| perl_ioctl() { |
| perl -e 'my $buffer; $buffer = pack(@ARGV[2], splice(@ARGV, 3)) if $#ARGV > 2; my $res; open(HANDLE, "<@ARGV[0]") && ioctl(HANDLE, @ARGV[1], $buffer) || die "@ARGV[0]: $!\n"; close(HANDLE); my @r_buffer = unpack(@ARGV[2], $buffer); exit unless @r_buffer; print STDOUT sprintf("0x%x\n" x @r_buffer, @r_buffer);' -- "$@" |
| } |
| get_flags() { |
| flags="`perl_ioctl "$1" $((EXT2_IOC_GETFLAGS)) L`" |
| } |
| set_flags() { |
| perl_ioctl "$1" $((EXT2_IOC_SETFLAGS)) L "$2" > /dev/null |
| } |
| else |
| echo "Need toolbox(1) or perl(1) to call ioctl(2)" 1>&2 |
| exit 1 |
| fi |
| |
| elf_ident="`dd if=\"$ioctl_program_path\" bs=1 skip=4 count=2 2> /dev/null`" || exit |
| case "${elf_ident%?}" in |
| "`printf '\\001'`") |
| flags_size=4 |
| ;; |
| "`printf '\\002'`") |
| flags_size=8 |
| ;; |
| *) |
| printf "Failed to determine register size of %s\\n" "$ioctl_program_path" 1>&2 |
| exit 1 |
| esac |
| case "${elf_ident#?}" in |
| "`printf '\\001'`") |
| flags_order=little |
| ;; |
| "`printf '\\002'`") |
| flags_order=big |
| ;; |
| *) |
| printf "Failed to determine byte order of %s\\n" "$ioctl_program_path" 1>&2 |
| exit 1 |
| esac |
| |
| parse_flags_string() { |
| local ORIG_IFS="$IFS" |
| IFS=, |
| set -- $1 |
| IFS="$ORIG_IFS" |
| set_flags=0 |
| clear_flags=0 |
| local f set_var_name clear_var_name |
| for f in "$@"; do |
| fn="${f#no}" |
| if [ "$fn" = "$f" ]; then |
| set_var_name=set_flags |
| clear_var_name=clear_flags |
| else |
| set_var_name=clear_flags |
| clear_var_name=set_flags |
| fi |
| case "$fn" in |
| simmutable) |
| f=$EXT2_IMMUTABLE_FL |
| ;; |
| sappend) |
| f=$EXT2_APPEND_FL |
| ;; |
| *) |
| [ -z "$f" ] && continue |
| printf 'chflags: invalid flag: %s.\n' "$f" 1>&2 |
| return 1 |
| esac |
| eval "$set_var_name=\$(($set_var_name|f))" |
| eval "$clear_var_name=\$(($clear_var_name&(~f)))" |
| done |
| } |
| |
| |
| case "$1" in |
| 0*) |
| replace_flags="$1" |
| ;; |
| =*) |
| parse_flags_string "${1#=}" || exit 255 |
| replace_flags="$set_flags" |
| ;; |
| *) |
| replace_flags= |
| parse_flags_string "$1" || exit 255 |
| ;; |
| esac |
| shift |
| [ -n "$no_fail" ] && exec 2> /dev/null |
| r=0 |
| chflags_loop() { |
| local level=$1 f new_flags skip |
| shift |
| [ $level -gt 0 ] && [ $# = 1 ] && [ "${1%/\*}" != "$1" ] && [ ! -h "$1" ] && [ ! -e "$1" ] && return |
| for f in "$@"; do |
| if [ -n "$no_follow" ] && [ -h "$f" ]; then |
| # Won't be able to ioctl(2) a symbolic link itself, skipping |
| if [ -z "$no_fail" ]; then |
| printf 'chflags: %s: Not following symbolic link\n' "$f" 1>&2 |
| r=1 |
| fi |
| continue |
| fi |
| skip= |
| if [ -n "$replace_flags" ]; then |
| new_flags="$replace_flags" |
| elif get_flags "$f"; then |
| new_flags=$(((flags|set_flags)&(~clear_flags))) |
| [ $new_flags = $((flags)) ] && skip=2 |
| else |
| #[ -z "$no_fail" ] && r=1 |
| [ -z "$recursive" ] && continue |
| skip=1 |
| fi |
| if [ -z "$skip" ] && set_flags "$f" $new_flags || [ -n "$no_fail" ]; then |
| [ -n "$verbose" ] && printf %s\\n "$f" |
| elif [ "$skip" != 2 ]; then |
| r=1 |
| fi |
| if [ -n "$recursive" ]; then |
| case "$recursive_follow" in |
| never) |
| [ -h "$f" ] && continue |
| ;; |
| explict) |
| [ $level -gt 0 ] && [ -h "$f" ] && continue |
| ;; |
| esac |
| [ -d "$f" ] && chflags_loop $((level+1)) "$f"/* |
| fi |
| done |
| } |
| chflags_loop 0 "$@" |
| exit $r |