3 .
/usr
/lib
/tool
/bash-utils ||
exit 1
9 command "$cmd" ${verbose:+-v} "$@"
14 if [ "$dryrun" = yes ]
16 [ "$1" != verbose
] ||
shift
25 [ "$debug" != yes ] || warnx
"$@"
30 echo "$PROGNAME: $*" >&2
35 "$@" && echo yes ||
echo no
52 local content
=`no_err_trap find "$1" -mindepth 1 -printf 1 -quit`
63 local access_code
=`echo_status no_err_trap [ -w "$1" ]`
65 if [ "$dryrun" != yes ]
67 if [ "$activewritetestonly" = yes -o \
( $access_code = 0 -a "$activewritetest" = yes \
) ]
70 while [ -z "$testfile" -o -e "$testfile" ]
72 testfile
=$1/takeown-writetest.
$RANDOM
74 access_code
=`echo_status no_err_trap touch "$testfile"`
75 [ ! -e "$testfile" ] ||
rm "$testfile" || true
83 return `echo_status no_err_trap [ "$recursive" = yes ]`
88 return `echo_status no_err_trap [ "$besteffort" = yes ]`
91 strip_trailing_slashes
()
94 while [ "${str: -1}" = / ]
103 return `echo_status no_err_trap [ "${strict[$1]}" = yes ]`
108 if [ $besteffort = yes ]
112 if [ $silent_strictness_check != yes ]
114 warnx
"Can not clone file's $1: $2"
116 return `echo_status no_err_trap [ "${strict[$1]}" != yes ]`
119 sort_by_length_desc
()
125 declare -a -g sort_by_length_desc_result
126 sort_by_length_desc_result
=()
131 for y
in "${sort_by_length_desc_result[@]}"
133 if [ ${#y} -le ${#x} ]
135 shiftindex
=${#sort_by_length_desc_result[@]}
136 while [ $shiftindex -ge $index ]
138 sort_by_length_desc_result
[$shiftindex]=${sort_by_length_desc_result[shiftindex - 1]}
139 shiftindex
=$
[shiftindex
- 1]
145 sort_by_length_desc_result
[$index]=$x
156 # Sort CLI arguments to make "dir1/file1" files processed earlier than "dir1/".
157 sort_by_length_desc
"${argv[@]}"
159 argv
=("${sort_by_length_desc_result[@]}")
163 for file in "${argv[@]}"
165 file=`no_err_trap strip_trailing_slashes "$file"`
167 local owner
=`no_err_trap stat -c %u "$file"`
169 if [ "$owner" != "$UID" ]
173 takeown symlink
"$file"
176 takeown directory
"$file"
186 # Replace filename in lines like: `# file: ...´
187 sed -e "s@^# file:.*@# file: ${1//@/\@}@"
192 # the file/directory to be taken own
193 local TAKEOWN_FILE
=$2
194 # the file/directory to be a clone of TAKEOWN_FILE (and supersede thereafter)
195 local TAKEOWN_COPY
=$2.takeown
196 # the file/directory TAKEOWN_FILE will be renamed to (can be used as backup)
197 local TAKEOWN_BACK
=$2.tookown
200 debug
"${FUNCNAME[0]}-$1 $2"
202 # clear tracking arrays
203 files_copied_out_src
=()
204 files_copied_out_trg
=()
205 files_moved_out_src
=()
206 files_moved_out_trg
=()
209 # Run individual takeown procedures in subshell to avoid cascade exception.
224 if [ $errstatus != 0 ]
226 if [ "$ignoreerrors" = yes ]
237 if [ $do_cleanup = yes ]
239 warnx
"Error happened. Reverting changes..."
243 run verbose
rm --force "$TAKEOWN_COPY"
246 run verbose
rm --force "$TAKEOWN_COPY"
251 warnx
"Removing copies..."
252 idx
=$
[ ${#files_copied_out_trg[@]} - 1 ]
253 for ((; idx
>=0; idx--
))
255 run verbose
rm --force "${files_copied_out_trg[idx]}"
258 warnx
"Moving back moved files..."
259 idx
=$
[ ${#files_moved_out_trg[@]} - 1 ]
260 for ((; idx
>=0; idx--
))
262 run verbose
mv --force --no-target-directory "${files_moved_out_trg[idx]}" "${files_moved_out_src[idx]}"
265 warnx
"Removing created directories..."
266 idx
=$
[ ${#dirs_created[@]} - 1 ]
267 for ((; idx
>=0; idx--
))
269 run verbose
rmdir "${dirs_created[idx]}"
272 # the last step of taking own a directory is to replace TAKEOWN_FILE with TAKEOWN_COPY.
273 # until this step we're working on and in TAKEOWN_COPY,
274 # so it's save to remove it.
275 if [ -e "$TAKEOWN_COPY" ]
277 run verbose
rmdir "$TAKEOWN_COPY"
280 if [ -e "$TAKEOWN_BACK" ]
282 run verbose
mv --force --no-target-directory "$TAKEOWN_BACK" "$TAKEOWN_FILE"
287 warnx
"Error happened. Keep files."
293 try
--except "cleanup symlink"
294 copy_out_symlink
"$TAKEOWN_FILE" "$TAKEOWN_COPY"
295 run verbose
mv --force --no-target-directory "$TAKEOWN_COPY" "$TAKEOWN_FILE"
301 try
--except "cleanup file"
302 copy_out_file
"$TAKEOWN_FILE" "$TAKEOWN_COPY"
303 run verbose
mv --force --no-target-directory "$TAKEOWN_COPY" "$TAKEOWN_FILE"
307 register_moved_file
()
309 files_moved_out_src
+=("$1")
310 files_moved_out_trg
+=("$2")
313 register_copied_file
()
315 files_copied_out_src
+=("$1")
316 files_copied_out_trg
+=("$2")
319 register_created_dir
()
326 if [ "$do_chgrp" = yes ]
328 run verbose chgrp
--quiet --reference="$1" --no-dereference "$2" || no_strict group
"$1"
330 # First chgrp then chmod to prevent to clear setgid bit.
331 run verbose
chmod --quiet --reference="$1" "$2" || no_strict mode
"$1"
332 getfacl
--skip-base "$1" | subs_file_comment
"$2" | run setfacl
--restore=- || no_strict acl
"$1"
333 getfattr
--no-dereference --physical --dump "$1" | subs_file_comment
"$2" | run setfattr
--restore=- || no_strict xattr
"$1"
334 # TODO support ext2 attributes
345 # run in subshell to able to check best-effort mode.
346 # enclose in try/untry to clear "cleanup" ERR trap.
353 copy_out_symlink
"$@"
359 warnx
"internal error: copy_out $filetype $*"
367 if [ $errstatus = 0 ]
369 register_copied_file
"$1" "$2"
375 if [ $do_cleanup = yes ]
377 # delete file failed to copy (at all or properly)
378 if [ -e "$COPYOUT_TRG" ]
380 run verbose
rm --force "$COPYOUT_TRG"
387 # return success. errors handled earlier.
396 run
touch --reference="$src" "$trg"
397 # setup a mode just enough to able to write. clone all attributes later.
398 run
chmod 0600 "$trg"
399 run verbose
cp --no-dereference --preserve=all
--force --no-target-directory "$src" "$trg"
400 copy_attributes
"$src" "$trg"
408 local symlink_target
=`no_err_trap readlink "$src"`
409 run verbose
ln --symbolic --no-target-directory --force "$symlink_target" "$trg"
410 run
touch --no-dereference --reference="$src" "$trg"
411 # symlinks does not seem to capable to carry ACLs on my system.
412 # I get EPERM on lsetxattr(2) on symlinks too.
415 takeown_directory_recursive
()
420 local do_supersede
=$4
423 local is_writable_src
425 run verbose
install -m 0700 -d "$trg"
426 # delay cloning attibutes to prevent unlucky cases
427 # such as "u-w" permissions on the directory
429 register_created_dir
"$trg"
433 for subfile
in "$src"/*
435 subfile
=${subfile##*/}
436 owner
=`no_err_trap stat -c %u "$src/$subfile"`
437 is_writable_src
=`no_err_trap yesno is_writable_dir "$src"`
439 if [ -L "$src/$subfile" ]
441 if [ "$owner" = "$UID" -a $is_writable_src = yes ]
443 if run verbose
mv --force --no-target-directory "$src/$subfile" "$trg/$subfile"
445 register_moved_file
"$src/$subfile" "$trg/$subfile"
447 # either 'mv' should succeed or we should be in best-effort mode
451 copy_out symlink
"$src/$subfile" "$trg/$subfile"
452 # best-effort mode checked and exceptions handled within copy_out func
455 elif [ -d "$src/$subfile" ]
457 # it's a directory ($subfile) within that directory
458 # which has to be taken own ($src).
460 # now, it's either ours ($subfile) and parent ($src) is writable,
461 # then it can be moved almost directly;
462 # or else it has be taken own as well.
464 if [ "$owner" = "$UID" -a $is_writable_src = yes ]
466 # directories must be writable in order to be moved
467 # into an other parent directory.
468 # so temporary allow the current user to write her
469 # own directory ($subfile) to able to move it.
471 local modes
=`no_err_trap stat -c %a "$src/$subfile"`
472 run
chmod u
+w
"$src/$subfile" ||
[ -w "$src/$subfile" ]
474 if run verbose
mv --force --no-target-directory "$src/$subfile" "$trg/$subfile"
477 # restore permission modes on $subfile (now it's in $trg directory)
478 run
chmod 0$modes "$trg/$subfile"
479 register_moved_file
"$src/$subfile" "$trg/$subfile"
482 # restore permission modes on $subfile (stayd in $src directory)
483 run
chmod 0$modes "$src/$subfile"
486 # assert the move was successful
487 test $moved = yes -o $besteffort = yes
489 # this directory ($subfile) can not be moved,
491 takeown_directory_recursive
"$src/$subfile" "$trg/$subfile" "" no
492 # best-effort mode is checked recursively
495 elif [ "$owner" = "$UID" -a $is_writable_src = yes ]
497 if run verbose
mv --force --no-target-directory "$src/$subfile" "$trg/$subfile"
499 register_moved_file
"$src/$subfile" "$trg/$subfile"
501 # either 'mv' should succeed or we should be in best-effort mode
505 copy_out
file "$src/$subfile" "$trg/$subfile"
506 # best-effort mode checked and exceptions handled within copy_out func
511 # clone attributes of the directory taken own just now
512 # as we finished writing into it.
513 copy_attributes
"$src" "$trg"
514 # best-effort mode checked and exceptions handled within copy_attributes func
516 # actually rename the owned directory
517 # to the one which is wanted to be taken own.
518 if [ "$do_supersede" = yes ]
521 if [ -n "$src_back" ]
523 if ! is_empty_directory
"$src"
525 run verbose
mv --force --no-target-directory "$src" "$src_back"
529 # final step to takeown
530 run verbose
mv --force --no-target-directory "$trg" "$src"
536 local do_supersede
=yes
538 try
--except "cleanup directory"
539 takeown_directory_recursive
"$TAKEOWN_FILE" "$TAKEOWN_COPY" "$TAKEOWN_BACK" $do_supersede
554 strict
=([mode
]='' [group
]='' [acl
]='' [xattr
]='')
558 activewritetestonly
=''
559 silent_strictness_check
=no
568 echo "Usage: takeown [options] <files>
571 -v, --verbose verbose
572 -n, --dry-run dry run
573 -R, --recursive recurse into directories
574 -w, --active-write-test active write test if access(2) reports writable
575 -W, --active-write-test-only active write test regardless of access(2), default is access(2) only
577 -i, --ignore ignore failures on files given in command arguments
578 -b, -I, --best-effort ignore any failure while recursing (force -i -T)
579 -C, --no-cleanup keep *.takeown files on failure
581 -T, --no-strict attempt but do not requisite file attribute preservation (default)
582 -M, --no-strict-chmod ignore unpreserved permission modes
583 -G, --no-strict-chgrp ignore unpreserved group owner
584 -A, --no-strict-setfacl ignore unpreserved ACL
585 -X, --no-strict-setfattr ignore unpreserved extended attributes
587 -t, --strict fail if can not preserve file attributes
588 -m, --strict-chmod requisite preserved permission modes
589 -g, --strict-chgrp requisite preserved group owner
590 -a, --strict-setfacl requisite preserved ACL
591 -x, --strict-setfattr requisite preserved extended attributes
592 --no-chgrp do not chgrp at all
595 0 all operations succeeded
596 1+ error happended during progress, even it was ignored"
611 -R|
--recursive|
--recurse)
614 --no-group|
--no-chgrp)
624 for s
in ${!strict[@]}
626 [ -z "${strict[$s]}" ] && strict
[$s]=yes
630 for s
in ${!strict[@]}
632 [ -z "${strict[$s]}" ] && strict
[$s]=no
635 -M|
--no-strict-mode|
--no-strict-modes|
--no-strict-chmod)
638 -G|
--no-strict-group|
--no-strict-chgrp)
641 -A|
--no-strict-acl|
--no-strict-setfacl)
644 -X|
--no-strict-xattr|
--no-strict-setfattr)
647 -M|
--strict-mode|
--strict-modes|
--strict-chmod)
650 --strict-group|
--strict-chgrp)
653 -A|
--strict-acl|
--strict-setfacl)
656 -X|
--strict-xattr|
--strict-setfattr)
659 -w|
--active-write-test)
662 -w|
--active-write-test-only)
663 activewritetestonly
=yes
666 errx
1 "Unknown option: $1"
680 # these arrays keep track of recursive directory takeown process.
681 declare -a files_copied_out_src
682 declare -a files_copied_out_trg
683 declare -a files_moved_out_src
684 declare -a files_moved_out_trg
685 declare -a dirs_created
700 takeown - Take ownership on files, even for unprivileged users
704 takeown [I<options>] <I<files> and I<directories>>
708 Command chown(1) or chown(2) is permitted only for root (and processes with CAP_CHOWN),
709 but normal users can imitate this behavior.
710 You can copy other users' file to your own in a directory writable by you,
711 and then replace the original file with your copy.
712 It is quite tricky and maybe expensive (copying huge files), but gives you an option.
713 Say, when somebody forgot to use the right user account when saving files directly to your folders.
715 takeown(1) uses B<*.takeown> and B<*.tookown> filename extensions to create new files
716 and to rename existing files to respectively.
718 See C<takeown --help> for option list.
720 =head1 TECH REFERENCE
724 script --> main --> takeown
728 takeown takeown takeown
729 _file _symlink _directory
731 - - - - - - | - - - - | - - - - - | - - - - - - - - - - - - - -
733 handler | | V ,---> register_created_dir
734 function: | | ,--> takeown |
735 cleanup | | | _directory ---+---> register_moved_file
737 | | | | | \ `---> register_
738 | | `------´ | \ ,-> copied_file
740 | copy_out <-- copy_out --\--'
743 copy_out <------------´ |
746 `-------> copy_attributes <-----´