Привет, %username%
! Довольно популярный вопрос на собеседовании, который задаю в том числе и я. У меня этот вопрос звучит дословно вот так: “Что будет, если выполнить в консоли из под root’a chmod a-x $(which chmod)
и как это починить?”
Когда мне впервые задали на собесе этот вопрос, я придумал 2 самых очевидных способа починить этот косяк – думаю их должны либо знать все, либо догадаться до этих способов (о них расскажу позже). А для начала расскажу что тут происходит:
- в первую очередь оболочка “вычисляет значение в скобочках”, а там у нас вызов команды
which
с аргументомchmod
, который возвращает путь до исполняемого файла (в нашем случаеchmod
) основываясь на переменной окруженияPATH
и показывает первое нахождение; - дальше запускается утилита
chmod
, которая отвечает за назначение прав доступа (или ACL) к файлам, которой в качестве первого аргумента передаетсяa-x
, а в качестве второго путь до исполняемого файла/bin/chmod
. Первый аргумент нам говорит о то, что “для всех битов доступа” (a
это короткая записьugo
, что является сокращением отuser
/group
/other
– “пользователь владелец”/“группа владельца”/“остальные”) нужно “отнять” (-
это отобрать биты доступа, а+
добавить биты доступа) бит доступа на “исполнение” (x
- executable,r
- read,w
- write/edit);
Примечание: утилита
which
с помощью флага-a
может отобразить абсолютно все найденные пути, а с помощью флага-s
просто проверить существование (хотя бы один доступный путь) – завершение сexit code 0
говорит о том, что исполняемый файл найден, а завершение сexit code 1
говорит о том, что исполняемый файл не найден.
Собственно это все, что происходит в строке chmod a-x $(which chmod)
. А теперь попробуем придумать, как можно это исправить.
Попытки исправить
Переложить содержимое файла
Первое, что должно приходить на ум:
- найти файл, который имеет execution bit и выполнить команду
cat $(which chmod) > /path/to/file_with_executable_bit
; - после чего, можно будет сделать
/path/to/file_with_executable_bit a+x $(which chmod)
;
Да, вот так легко и просто можно исправить свой косяк (или не свой). Так же локально можно использовать rsync --chmod=777
– да, rsync
умеет выставлять принудительно права на файлы.
Скопировать атрибуты
Стандартная утилита cp
умеет перекладывать не только сами файлики, но и переносить отдельно их атрибуты:
cp --attributes-only --preserve=mode /bin/chown /bin/chmod
Скопировать с другого сервера
Наверняка, кто-то скажет (и это самый популярный почему-то вариант): скопировать с другой системы. И это можно сделать двумя способами:
- с помощью утилиты
scp
с флагом-p
; - с помощью утилиты
rsync
с флагами-av
;
Второй вариант – rsync
– более предпочтителен, т.к. сохранит еще и пользователя и группу, а scp
этого не сделает. Но учитывая, что в рамках задачки мы работаем под root’ом – можно считать эти варианты идентичными.
Однострочники на ЯПах
Да, этот способ – это очередное подтверждение того, что ops должен уметь кодить (хотя бы на уровне говнокода) – о том, почему это надо более подробно я упоминал тут.
Собственно к решению - выполняем вот такую грязь:
- на python3:
python3 -c "import os; os.chmod('/bin/chmod', 0o755)"
- на python2:
python2 -c "import os; os.chmod('/bin/chmod', 0755)"
- на perl:
perl -e "chmod 0755, "/bin/chmod"; exit;
- на php:
php -r 'chmod("/bin/chmod", 0755);'
- на ruby:
ruby -r fileutils -e "FileUtils.chmod 0755,'/bin/chmod'"
Как видишь, довольно просто всё – в каждом из популярных языков имеется свой функционал, с помощью которого можно назначить права на файл.
Golang
На Golang можно сделать таким же простым способом – положи код из примера ниже в файл chmodfixer.go
:
// chmodfixer.go
package main
import (
"log"
"os"
)
func main() {
var perm fs.FileMode
perm = 0755
file := "/bin/chmod"
err := os.Chmod(file, perm)
if err != nil {
log.Fatalf("can't set file permissions for %s with err: %s", file, err)
}
log.Printf("file permissions for %s is set to %d", file, perm)
}
Выполнение в консоли go run fix.go
просто запустит данный “скрипт”, а go build -o /tmp/chmodfixer fix.go
скомпилирует данный код, а исполняемый файл сохранит в /tmp/chmodfixer
.
Midnight Commander aka mc
Не особо любимый мною “навигатор по файловой системе” может пригодиться в решении данной проблемы – запустим его в нужной директории mc /bin
. Надо всего лишь пройти в меню Press F9
-> File
-> Chmod
, а дальше выставить все права – читай внимательно и все будет норм. Если проделать все те же самые действия, но на горячих клавишах – будет вот такая комбинация:
C-s chmod
–C-s == Ctrl+s
найти файл;C-x c rwxdbhs
–C-x c == Ctrl+x c
назначить права;
Редакторы Emacs и VIM
Да, можно использовать редакторы и для на столько неожиданных вещей. Начнем с Emacs и запустим его – emacs /bin
, а дальше:
C-s chmod
;M 755
А на VIM’e можно сделать в одну строку вообще, вот так – vim -e --cmd "call setfperm('/bin/chmod','rwxr-xr-x')|q"
.
Однострочник на тонких сях
Отдельно стоит упомянуть про tcc
– tiny C compiler. Не самый очевидный выбор, но как вариант решения проблемы – бронебойный. Сделать можно вот так – буквально в одну строку (поставим права 0755
):
tcc -run <(echo '#include <sys/stat.h>'; echo '#define CHMODFILE "/bin/chmod"'; echo 'void main() { chmod(CHMODFILE, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH); }')
Изучить, как определить права можно в исходных кода библиотеки sys/stat.h
на GitHub в репозитории torvalds/linux.
Reinstall package
Можно переустановить пакет, который содержит нужный нам бинарник – при установке всегда назначаются правильные/корректные права на файлы из пакета. Сделать можно вот так:
- RHEL-based системы:
dnf reinstall $(rpm -qf /bin/chmod)
; - Debian-based системы:
apt reinstall $(dpkg-query -S bin/chmod|cut -d: -f1)
LD SO
Можно задействовать некоторые библиотеки, например на Ubuntu 20.04 с архитектурой x86_64 можно сделать вот такой финт ушами:
sudo /lib64/ld-linux-x86-64.so.2 /bin/chmod 755 /bin/chmod
Архиватор tar
Внезапный кандидат – архиватор tar
– решает проблему буквально в одну строку:
tar -cP --mode=755 /bin/chmod | tar x
Для разнообразия
Стоит так же упомянуть про такие штуки:
- ctypes.sh – небольшая библиотека, написанная на чистом C, которая позволит в bash-функциях делать некоторые вещи, в том числе и назначать права на файлы;
- gdb – GNU Linux Debugger, с помощью которого можно так же дернуть нужный syscall и выставить нужные права;
На форуме Linux Questions собраны самые разнообразные способы решения – большая часть пересекается с данной статьей.
А если интересно посмотреть какие бывают вызовы ядра в Linux, то можешь посмотреть вот сюда – тут очень большой список, а главное со ссылками на конкретные реализации в коде.
Итого
Когда я впервые услышал такой вопрос на собеседовании, первое что я ответил было “Я бы проверил свой рацион на предмет наличия запрещенных веществ”. В реальном мире попробовать такое запустить можно разве что ради эксперимента, либо по случайной ошибке.
Основная задача данного вопроса состоит не в количестве придуманных вариантов решения, а в проверке хода мышления в самой нестандартной ситуации при решении проблемы, с которой точно не сталкивался человек.
Если у тебя есть вопросы, комментарии и/или замечания – заходи в чат, а так же подписывайся на канал.