Я использую систему GNU/Linux.
Во-первых, у меня есть:
Перенесено в каталог /tmp/.
Создал файл с именем ruby.rb как пользователь без полномочий root.
Открыт irb как пользователь su.
Сейчас в ИРБ:
┌┄┄[root::archlinux]┈[/tmp]
└──╼⮚ irb
irb(main):001:0> File.writable?('ruby.rb')
=> true
irb(main):002:0> File.stat('ruby.rb')
=> #<File::Stat dev=0x2d, ino=819138, mode=0100644, nlink=1, uid=1000, gid=1000, rdev=0x0, size=0, blksize=4096, blocks=0, atime=2019-07-14 04:44:13 +0530, mtime=2019-07-14 04:44:13 +0530, ctime=2019-07-14 04:44:13 +0530>
irb(main):003:0> File.write('ruby.rb', '#!/usr/bin/ruby -w')
Traceback (most recent call last):
3: from /root/.irb:351:in `<main>'
2: from (irb):3
1: from (irb):3:in `write'
Errno::EACCES (Permission denied @ rb_sysopen - ruby.rb)
irb(main):004:0>
Помимо этого, на самом деле я пытаюсь написать файл журнала. Я проверяю, доступен ли файл для записи. Если нет, он отправляет уведомление пользователю.
Раньше я сталкивался с проблемой, когда File#writable?(str) возвращал true в таком случае (и это работает в каталоге /tmp/). И просто использовал блок begin <...> rescue Errno::EACCES, чтобы решить проблему. Но в текущем проекте я не хочу использовать спасательный блок.
Почему File#writable?(str) вообще возвращает true?
Обновлено: Прежде всего, это должно быть перемещено в unix.stackexchange.
Во-вторых, я понял
Вы не можете изменить файл в файловой системе tmp: я смонтировал раздел размером всего 4 МБ как tmpfs в /mnt. Создал файл как локальный пользователь. Потом сменил разрешение на всемогущее 777! Затем я изменил свою учетную запись на root. Я попытался отредактировать содержимое с помощью nano. Это невозможно.
Файл Руби#доступен для записи? вероятно, не определяет тип смонтированной файловой системы. Будь то XFS или EXT4, procfs или tmpfs. Если все, что он делает, это проверяет режим, в таком сценарии File#writable? возвращает true, в то время как на самом деле он не доступен для записи!
Редактировать 2: я загрузил 2 скриншота на imgur:


Этот вопрос должен быть в теме Stack Overflow. Проблема в том, что отсутствует Минимальный, полный и проверяемый пример. File#writable?(str) недостаточно информации, чтобы помочь вам.
Во многих системах /tmp не является обычной файловой системой, поэтому вы не сможете использовать ее напрямую.
/tmp доступен для записи в популярных дистрибутивах Linux. вы можете запустить strace -o $HOME/trace irb и повторить сеанс. Вывод в $HOME/trace irb сообщит вам, что irb просит ОС сделать и что ОС отвечает. Могут быть проблемы со списком контроля доступа или файловая система может быть смонтирована только для чтения. Я полагаю, что «правильный» способ реализации File#writable — через вызов ОС access(2) — strace(1) должен сообщить вам, если это так.
@root - Не всегда верно. Большинство машин в Компиляционная ферма GCC — это Linux, но они не имеют права записи пользователем /tmp. Если у вас есть учетная запись на ферме, вам нужно export TMPDIR = "$HOME/tmp", если вы планируете использовать временный каталог.
Я не могу воспроизвести это. Вот что я пробовал (без полномочий root): sudo mount -t tmpfs none /mnt, echo foo > /mnt/myfile, chmod 777 /mnt/myfile, sudo bash -c 'echo more >> /mnt/myfile'. Согласно вашему сообщению, я должен получить сообщение об ошибке, но вместо этого он завершается с успехом и cat /mnt/myfile показывает «foo more», как и ожидалось.
@root, /tmp доступен для записи. Но вы написали файл с именем a от имени пользователя x, насколько я понял, вы не можете записать файл от имени пользователя y даже с правами 666... Странно. Моему скрипту нужны привилегии root, чтобы выполнить какую-то задачу. Но что ж, я мог бы хотя бы проверить, существует ли уже файл и принадлежит ли он пользователю root или нет в Ruby...
Ну, на самом деле у меня есть 2 других компьютера. Такое поведение наблюдается на моем ноутбуке с Arch x86_64. Просто чтобы убедиться, я попробовал свой raspberry pi под управлением Arch Linux ARM. Шаги: (1) Перейдите в /tmp. (2) touch file (3) chmod 777 file. (4) su (5) nano file Такое же поведение наблюдается и на Pi. Если вы видите разрешения, это 777, и любой может читать, писать и выполнять их. Но если файл находится в /tmp/, другие пользователи (даже root) не могут писать в него. Я запускаю Kernel 5.2.0-arch2-1-ARCH на ноутбуке и 4.19.57-1-ARCH на Raspberry Pi 3 модели B.
@thatotherguy, да, это странно. Если вы наберете echo > file да, это сработает. Но редакторы nano, vi TUI и редакторы GUI Geany, Mousepad, atom не могут писать в него. Как только вы сделаете echo > file как root, вы не сможете написать его как обычный пользователь в этих редакторах. Да, ruby тоже не может записать файл. Работает только переадресация!
Привет, я загрузил 2 скриншота на imgur: imgur.com/tB4T5Jlimgur.com/hzc5s27
(1) какой дистрибутив вы используете на своем ноутбуке? (2) каков результат test -w /tmp/ruby.rb; echo $?? (3) каков результат su -c 'test -w /tmp/ruby.rb; echo $?'? (4) каков результат mount | grep /tmp? (5) каков результат getfacl /tmp/ruby.rb? (6) каков результат ls -ld /tmp?
Я могу воспроизвести это в ArchLinux, но не в любом другом дистрибутиве.
Такой же. У меня есть 3 системы под управлением Arch Linux. Поэтому я подумал, что это можно воспроизвести в системе. На самом деле тогда я сказал своему другу (который использует Ubuntu) сделать это. Он сказал, что все идет так, как ожидалось. Затем я запустил свою виртуальную машину Linux Mint, и мой вопрос перестал быть актуальным. Простите. Я не знал, что это может быть только для Arch Linux. Я не знаю почему. Позвольте мне опубликовать это на форуме Arch Linux! Не беспокойтесь о минусах ;) ... Как всегда узнал что-то новое ;)
Хорошо, вот что я нашел: bbs.archlinux.org/viewtopic.php?id=244482

Может быть одна из двух вещей: файл не открывается для записи или проблема с правами пользователя.
Попробуйте открыть файл для записи с помощью 'w' или 'w+'
File.open('ruby.rb', 'w') { |file| file.write("#!/usr/bin/ruby -w") }
Если это не сработает, попробуйте использовать chmod для обновления прав доступа к файлам для ruby.rb с помощью
chmod -R 777 <path_to_file>
Нет, File.open... не работает. Ну да, я могу потребовать fileutils и добавить FileUtils.chmod(0777, 'ruby.rb') и все равно иметь ту же проблему. Я тоже подумывал сначала сделать 0666. Вы знаете, что сначала было установлено разрешение 0644, что означает, что оно было доступно для записи! В любом случае не работает! Я столкнулся с этой проблемой только в каталоге /tmp/...
Хорошо, это не специфичная для Ruby проблема. Тем не менее, у любого языка программирования будет такая проблема, потому что все дело в защите файловой системы, реализованной в systemd.
Опция fs.protected_regular реализована в ядре Linux 4.19+ чтобы усложнить атаки спуфинга данных.
Итак, в первую очередь нужно проверить:
sysctl fs.protected_regular
В вашем случае он должен печатать 1
Итак, измените значение на 0:
sysctl fs.protected_regular=0
Надеюсь проблема решится. Или попробуйте это:
sysctl fs.protected_regular=0
sysctl fs.protected_fifos=0
Обратите внимание, что эта опция включена по умолчанию. Возможно, вам не захочется говорить каждому пользователю вашей программы, чтобы он установил вышеприведенное значение 0! Они могут даже не предпочесть это!
Итак, в Руби:
File.delete('file') if File.exist?('file')
# Better use the '/tmp/file' or File.join(%w(/ tmp file)) otherwise the file will get delete from the Dir.pwd
Который будет удалять файл каждый раз, когда запускается скрипт.
begin ... rescue и использовать Warning.warn или Kernel.warn или STDERR.puts для печати предупреждающего сообщения.
Почему бы не использовать ruby-doc.org/stdlib-2.4.0/libdoc/tempfile/rdoc/Tempfile.html?