Почему монтирование происходит поверх существующего каталога?

52

Существующий каталог необходим в качестве точки монтирования .

$ ls
$ sudo mount /dev/sdb2 ./datadisk
mount: mount point ./datadisk does not exist
$ mkdir datadisk
$ sudo mount /dev/sdb2 ./datadisk
$

Я нахожу это сбивающим с толку, поскольку он перекрывает существующее содержимое каталога. Существует два возможных содержимого каталога точек монтирования, которые могут неожиданно переключиться (для пользователя, который не выполняет монтирование).

Почему не mountпопадает во вновь созданный каталог? Именно так графические операционные системы отображают сменные носители. Было бы ясно, если каталог смонтирован (существует) или не смонтирован (не существует). Я уверен, что есть веская причина, но я пока не смог ее обнаружить.

Melebius
источник
1
Если вы хотите такое поведение, используйте udisksctl. Зачем использовать mount?
Муру
1
Потому что это путь Unix. Потому что таким образом он более гибкий, и вы можете монтировать его в любом месте. Потому что их монтирование в любом месте позволяет вам расширять свои серверы по мере необходимости, например, получить новый диск для раздела базы данных, переместить данные в разделе БД на новый диск и смонтировать его в нужном месте, чтобы позволить данным БД расти больше.
Руи Ф Рибейро
8
Как историческая справка, до того, как Windows и LInux по сути уничтожили все остальные ОС, существовала компания под названием Apollo. Они написали Unix-подобную (лучший дизайн, чем Unix!) Операционную систему. Он создал каталоги, в которые экспорты NFS были смонтированы автоматически. На самом деле, вы не можете подключиться к уже существующему каталогу. HP купила Apollo, выбросила операционную систему и использовала 64-битный процессор Apollo в качестве HP-PA. Система удаленных вызовов процедур Apollo стала DCE OSF, которая, очевидно, живет внутри Windows. Знание - это полдела!
Брюс Эдигер
как-то это происходит в моей системе Ubuntu 14.04,3. Я еще не исследовал. когда моя SD-карта монтируется, она заканчивается на пути, который не имеет ничего под ней. если я отключу его и попытаюсь смонтировать вручную, я получаю сообщение об ошибке: в точке монтирования нет каталога.
Skaperen
2
@BruceEdiger better design than Unix![необходима цитата]
Руслан

Ответы:

51

Это случай утечки деталей реализации.

В системе UNIX каждый каталог состоит из списка имен, сопоставленных с номерами inode . Inode содержит метаданные, которые сообщают системе, является ли это файл, каталог, специальное устройство, именованный канал и т. Д. Если это файл или каталог, он также сообщает системе, где найти содержимое файла или каталога на диске. Большинство инодов являются файлами или каталогами. -iВариант lsсписок будет номера индексных дескрипторов.

Монтирование файловой системы берет индексный каталог и устанавливает флаг в копии ядра в памяти, чтобы сказать «на самом деле, при поиске содержимого этого каталога вместо этого смотрите на эту другую файловую систему» ​​(см. Слайд 10 этой презентации ). Это относительно просто, так как это изменение одного элемента данных.

Почему он не создает запись каталога для вас, указывая вместо этого на новый индекс? Это можно реализовать двумя способами, оба из которых имеют свои недостатки. Один из них - физически записать новый каталог в файловую систему, но это не даст результатов, если файловая система доступна только для чтения! Другой - добавить в каждый процесс листинга каталога список «лишних» вещей, которых на самом деле нет. Это неудобно и может привести к небольшому снижению производительности при каждой файловой операции.

Если вам нужны динамически созданные точки монтирования, automountсистема может сделать это. Специальные файловые системы без диска могут также создавать каталоги по желанию, например proc, sys, devfsи так далее.

Изменить: см. Также ответ на вопрос « Что происходит, когда вы« монтируете »существующую папку с содержимым?

pjc50
источник
За исключением того, что он не устанавливает флаг на inode. sudo mount --bind / /mnt ; ls /mnt/proc-> пусто. Интересно, как это работает.
sourcejedi
Точная операция fs/namespace.c, я думаю; Я не знаком с источником и не хотел тратить слишком много времени на детализацию. «Флаг на иноде» я получил из связанной презентации.
pjc50
2
@sourcejedi: bind mounts привязывает только ту файловую систему, на которую вы ссылаетесь. Они не рекурсивно связывают другие файловые системы, смонтированные под ним. Это удобный способ найти спрятанный под креплением хлам. (Например, если какой-то материал попал на корневую ФС через /var/cacheкакое-то время, когда /varне удалось смонтировать.) Смотрите также path_resolution(7). (У старых linux-manpages эта страница руководства была в разделе 2, как die.net) IDK, как Linux на самом деле работает внутри, чтобы оптимизировать проверку каждого компонента каталога как возможного монтирования. Может быть, прикрепить эту запись VFS в кэш?
Питер Кордес,
2
Правильно, это моя точка зрения ... Так что fs/namei.c(путь -> поиск по индоду) вызывает в namespace.c хотя lookup_mnt(). На dentry есть флаг (запись в кэше каталога). Но это всего лишь оптимизация, известная как реализация. Он не говорит вам, какая файловая система там смонтирована; Вы должны посмотреть в таблице монтирования. (См. M_hash (), для более подробной информации о реализации. Linux, по крайней мере, избегает дополнительных сравнений строк, и AFAICS в то же время удается повторно использовать dentry, например, для bind mounts, потому что он написан мастерами).
sourcejedi
1
@PeterCordes man 8 mount:: mount --bind foo foo. mountВызов bind присоединяет (частично) только одну файловую систему, а не возможные дополнительные монтирования. Вся файловая иерархия, включаяmount --rbind olddir newdir
подмонтирования,
19

Если в качестве точки монтирования mount(2) требовалось создать новый каталог, вы не могли монтировать что-либо в файловой системе только для чтения. Это было бы глупо, поэтому мы можем исключить это.

Если mount при необходимости создает новый каталог для точки монтирования, это будет странно. Это не так, как монтирование / размонтирование происходит постоянно, поэтому добавление дополнительной логики в ядро ​​для выполнения этих двух шагов с помощью одного системного вызова не будет важным ускорением. Просто оставьте это в пользовательском пространстве, чтобы сделать mkdir(2)системный вызов, если он этого хочет. В ответе Дмитрия указывается, что mount(2)выполнение обоих этих действий сделает его неатомарным. И вы хотите дополнительный аргумент mount(2)с режимом флагов , как open(2)требуется, для O_CREAT, O_EXCLи т.д. Это будет просто глупо по сравнению с позволяя пользовательское пространства сделать это.

Или, может быть, вы спрашивали о том, чтобы mount(8)(традиционная программа, mount(2)выполняющая системные вызовы) это делала? Это было бы возможно, но это уже идеально подходит mkdir(1)для этой работы, а дизайн Unix - это хорошие небольшие инструменты, которые можно комбинировать. Если вам нужен инструмент, который выполняет обе функции, то легко написать сценарий оболочки, чтобы построить этот инструмент из двух более простых инструментов. (Или, как прокомментировал muru, это udisksctlуже сделано , поэтому вам не нужно его писать.) Кроме того, обычный Linux mount(8)из util-linux поддерживает mount -o x-mount.mkdir[=mode]использование своего x-синтаксиса для параметров для пользовательского пространства, а не для параметров, передаваемых в файловую систему.


Теперь более интересный вопрос: почему в родительской файловой системе вообще должен быть каталог?

Как указывает ответ pjc50 (никакого отношения, даже если у него есть мои инициалы!), Отображение точек монтирования в списках каталогов потребует дополнительной проверки для каждого readdir().

Наличие точек монтирования существует в виде каталогов в директории, содержащей их (на родительской FS), это хороший трюк. readdir()не должен заметить, что это точка монтирования вообще. Это происходит только в том случае, если точка монтирования используется в качестве компонента пути. Разумеется, разрешение пути должно проверять таблицу монтирования для каждого компонента каталога пути.

Питер Кордес
источник
1
If mount(2) required the creation of a new directory to be the mount point, you couldn't mount anything under a read-only filesystem. That would be dumb- Я бы сказал, что это умнее: с точки зрения пользователя, файловая система,
доступная
2
@Izkata: Создание файловой системы только для чтения не означает, что все поддерево VFS заморожено. Он может иметь символические ссылки, указывающие на каталоги для чтения и записи, или уже иметь точки монтирования для чтения и записи под ним, когда родительский fs был перемонтирован ro. Существует много вариантов использования файловых систем только для чтения, где ваш аргумент не имеет смысла.
Питер Кордес
2
man 8 mount: x-mount.mkdir[=mode] Позвольте сделать целевой каталог (MOUNTPOINT). Необязательный аргумент mode определяет режим доступа к файловой системе, используемый mkdir(2)в восьмеричной записи. Режим по умолчанию - 0755. Эта функция поддерживается только для пользователей root.
mikeserv
Я не вижу каких-либо важных случаев использования файловых систем только для чтения со смонтированными файловыми системами для чтения и записи, особенно в ранних версиях Unix. @PeterCordes
kubanczyk
@kubanczyk: корневая файловая система только для чтения, с возможностью чтения /tmpи записи и /home. Или только для чтения NFS-монтируется /usrс локальным /usr/localмонтируется на нем. Или, в более общем смысле, любое общее изображение только для чтения с изменяемой частью, установленной поверх него. (Локальные моды для образа только для чтения также можно создавать для каждого файла с помощью пользовательских файловых систем, таких как overlayfs или другие объединенные файловые системы для Linux, используемые в загрузочных образах LiveCD.) Сначала я думал о корневой FS, изначально смонтированной RO, в загрузиться, но сделать это можно раньше других монтировок.
Питер Кордес
12

Монтирование в существующий каталог делает вызов mountпрактически атомарным: он либо успешно, либо неудачно, по крайней мере, с точки зрения пользователя. Если бы mountпришлось создавать саму точку монтирования, у него было бы две точки отказа, что делало невозможным гарантированный чистый откат. Представьте себе следующий сценарий:

  1. mount успешно создает точку монтирования
  2. mount пытается смонтировать новую файловую систему в этот каталог, но не удается
  3. mount пытается удалить точку монтирования, но не удается

Система заканчивается побочным эффектом отказа mount.

Вот еще один:

  1. umount успешно размонтирует файловую систему
  2. umount пытается удалить точку монтирования, но не удается

Теперь должен umountвернуть успех или неудачу?

Дмитрий Григорьев
источник
5
mountимеет 8 различных кодов возврата для ошибок, которые также могут быть объединены. Он может просто добавить еще один, когда удалить каталог не удается. man7.org/linux/man-pages/man8/mount.8.html#RETURN_CODES
хаос
8
Я думаю, что OP спрашивает, почему точка монтирования вообще должна быть существующим каталогом, а не почему mountсистемный вызов не создает его. Хотя, возможно, это была только моя интерпретация / ожидание того, что я думал, что ОП хотел спросить, или то, что я бы спросил, если бы я спрашивал.
Питер Кордес
3

Еще один случай, который может произойти:

При загрузке базовый образ только для чтения загружается в корневой каталог. Таким образом, вы хотели бы переопределить его, когда вы хотите найти настоящий корень. Таким образом, вы можете себе представить, что системный вызов mount просто меняет точку roмонтирования на rw.

Давайте представим, что у вас проблема с файловой системой в корневой точке монтирования, и вы хотели бы попытаться исправить ее. С помощью mount overlap вы можете размонтировать файловую систему и использовать fsckпредоставленный базовый образ для ее решения.

Эта функция также может быть полезна в системах, которые нуждаются в строгой защите, чтобы отслеживать изменения между roразделами и разделами rw.

alexises
источник
1
Я не уверен, как это отвечает на вопрос. Вы указываете на то, что при mount необходимости создания нового каталога в месте точки монтирования вы не можете ничего монтировать поверх файловой системы только для чтения? Первый абзац сбивает с толку: Linux initrd работает не так. Он использует pivot_rootсистемный вызов для изменения корневого fs, а не только для монтирования поверх него большего количества вещей. Это затрудняло следование вашей логике в следующих параграфах, потому что я думал, что вы говорите pivot_root(2).
Питер Кордес
2
@PeterCordes - Linux много лет не использовал initrd : при переключении другого корневого устройства, initrd будет, pivot_rootа затем umountи виртуальный диск. Но initramfs - это rootfs: вы не можете ни pivot_rootrootfs, ни размонтировать его . Вместо этого удалите все из rootfs, чтобы освободить пространство ( find -xdev / -exec rm {} \;), перемонтируйте rootfs с новым root ( cd /newmount; mount --move . /; chroot .), присоедините stdin / stdout / stderr к новой / dev / console и execновойinit
mikeserv
@mikeserv: аккуратно! Я не осознавал, что основной механизм переключения корней изменился, когда мы начали использовать initramfs вместо initrd. С точки зрения «убедитесь, что правильные модули ядра попали в него», они идентичны>. <. Я все еще думаю, что это не очень хорошо отвечает на вопрос . Кажется, предполагается, что интерпретация «монтировать под rofs невозможна», и дает очень специфический проблемный случай (что кажется маловероятным, потому что initramfs не монтируется только для чтения при загрузке. И даже если это так, его можно просто перемонтировать для чтения -пишите, не влияя на изображение cpio.gz.)
Питер Кордес
@PeterCordes - я действительно не понимаю этот ответ. я только что видел ваш комментарий - initramfs - это файловая система - она ​​не может быть доступной только для чтения - ее кэш fs воплощен.
mikeserv
2

Я тоже всегда удивлялся этому.

Простая обертка, такая как:

#!/bin/sh
eval "mkdir -p \"\$$#\"" 
/bin/mount "$@"  

сохраненный как исполняемый скрипт, названный mountв директории, переопределяющей /binв вашем PATH, должен позаботиться об этом, если это слишком беспокоит вас

(Перед запуском фактического mountдвоичного файла он создает каталог с именем после последнего аргумента mount, если такой каталог еще не существует.)


В качестве альтернативы, если вы не хотите, чтобы неудачные вызовы mountоболочки создавали каталоги, вы можете сделать:

#!/bin/sh
set -e
eval "lastArg=\"\$$#\""
test -d "$lastArg" || { mkdir "$lastArg"; madeDir=1; }
/bin/mount "$@"  ||  {  test -z "$madeDir" || rmdir "$lastArg"; }
PSkocik
источник
Разве mountкоманда не должна использовать созданный каталог?
Муру
1
@muru Вот что делает последняя строка.
PSkocik
Ах, вы имеете в виду , что следует использовать таким образом: mount /dev/foo /some/path? Я предполагал, что это будет работать так, как udisksctlработает, поэтому вы будете бежать mount /dev/foo.
Муру
4
Вы можете получить последний аргумент cmdline без evalрасширения $#, используя "${@:-1}". Я протестировал это с DASH, так как я думаю, что он не поддерживает ничего, кроме того, что требуется для поддержки POSIX sh. /bin/dash -c 'echo ${@:-1}' foo barотпечатки bar.
Питер Кордес
1
Вы можете использовать man -o x-mount.mkdir...
Mikeserv