Монтирование SMB/CIFS из PHP: mount.cifs, файл учётных данных и размонтирование
Зачем монтировать из PHP
Иногда нужно прочитать или разложить файлы на сетевой шаре без постоянного «ручного» mount на сервере: разовый импорт, синхронизация каталога из SMB, служебный скрипт на том же хосте, что и 1С‑Битрикс. Обертка в классе позволяет создать точку монтирования под конкретный UNC‑путь, подставить логин и пароль через файл опций и по завершении работы снять монтирование, чтобы не оставлять лишние записи в /proc/mounts.
Что должно быть на стороне ОС
На Linux обычно нужен пакет с mount.cifs (семейство cifs-utils). Пользователь, от которого крутится PHP‑процесс, должен иметь право вызывать монтирование — часто это root или настроенный sudo на конкретную команду; без этого PHP получит ошибку даже при корректном коде.
Схема работы
UNC вида //server/share/path превращается в локальную папку под выбранным базовым каталогом. Имя подкаталога можно привязать к хэшу строки шары, чтобы не пересекаться с другими задачами. Учётные данные пишутся во временный файл в формате ключ=значение построчно; в опциях монтирования указывается credentials=…. Для сценария «только чтение» разумно добавить ro. После работы — umount, удаление пустой точки и файла с паролем.
Пример класса
Ниже — самодостаточный каркас: конструктор принимает путь к шаре и массив строк для credentials, метод attach() создаёт монтирование, detach() снимает его. Деструктор вызывает detach(), чтобы не забыть размонтировать при unset или выходе из блока.
<?php
class NetworkShareMount {
private string $uncPath;
/** @var string[] */
private array $credentialLines;
private bool $verbose;
private ?string $baseDir = null;
private ?string $localMount = null;
private ?string $secretFile = null;
public function __construct(string $uncPath, array $credentialLines, bool $verbose = false) {
$this->uncPath = $uncPath;
$this->credentialLines = $credentialLines;
$this->verbose = $verbose;
}
public function __destruct() {
$this->detach();
}
/** @throws \Exception */
public function attach(string $baseDir): self {
$this->baseDir = rtrim($baseDir, '/');
$token = md5($this->uncPath);
$this->localMount = $this->baseDir . '/' . $token;
$this->secretFile = $this->baseDir . '/' . $token . '.cred';
if ($this->isActive()) {
$msg = 'Уже смонтировано в ' . $this->localMount;
$this->log($msg);
throw new \Exception($msg, 1);
}
$this->performMount();
$this->log('Смонтировано: ' . $this->localMount);
return $this;
}
public function getLocalPath(): string {
return (string) $this->localMount;
}
public function detach(): self {
if (!$this->isActive()) {
return $this;
}
exec(sprintf('umount %s', escapeshellarg($this->localMount)));
@rmdir($this->localMount);
if (is_file($this->secretFile)) {
unlink($this->secretFile);
}
$this->log('Размонтировано: ' . $this->localMount);
return $this;
}
private function isActive(): bool {
return $this->localMount !== null && is_dir($this->localMount);
}
/** @throws \Exception */
private function performMount(): void {
$body = implode("\n", $this->credentialLines) . "\n";
if (file_put_contents($this->secretFile, $body) === false) {
throw new \Exception('Не удалось записать credentials: ' . $this->secretFile);
}
chmod($this->secretFile, 0600);
mkdir($this->localMount, 0700, true);
$cmd = sprintf(
'/sbin/mount.cifs %s %s -o ro,credentials=%s && echo OK',
escapeshellarg($this->uncPath),
escapeshellarg($this->localMount),
escapeshellarg($this->secretFile)
);
$out = exec($cmd);
if (trim((string) $out) !== 'OK') {
throw new \Exception('Ошибка mount.cifs: ' . $this->uncPath);
}
}
private function log(string $text): void {
if ($this->verbose) {
echo $text . PHP_EOL;
}
}
}
$share = new NetworkShareMount(
'//file-host/projects/',
['username=backup_reader', 'password=********', 'domain=WORKGROUP'],
true
);
$mountPath = $share->attach(__DIR__)->getLocalPath();
// … работа с файлами в $mountPath …
$share->detach();Безопасность и ограничения
Файл с паролем держите с узкими правами (0600), каталог для монтирования не публикуйте в веб. Проверка «смонтировано ли» только через is_dir — упрощение; в ответственных сценариях имеет смысл сверяться с /proc/mounts или выводом findmnt. Не храните боевые пароли в репозитории: подставляйте их из окружения или секрет-хранилища хостинга.
Не хотите копаться сами?
Починю за 1-3 дня. Без предоплаты — оплата по результату.
15+ лет опыта с 1С-Битрикс · Без предоплаты · 7 дней гарантии