Skip to content
ENTERPRISE Enterprise technology · Engineered for your business Enterprise Software · Managed Servers · Business E-mail · LiteSpeed + CloudLinux · Imunify360 Security · cPanel Control · Network profile up to 3 Gbps on eligible plans
AIOR

DirectAdmin "An error occurred during the backup" — Fixing Hard Links

Aior

Administrator

Staff member
Joined
Apr 2, 2023
Messages
929
Reaction score
2
Points
18
Age
41
Location
Turkey
Website
aior.com
Thread owner

Önce bir düzeltme: Bu konunun Türkçe başlığındaki "sabit limitleri" ifadesi hatalı bir çeviri. Hata, disk kotasının sabit limitiyle (hard limit) değil, sabit bağlantılarla (hard link), yani birden fazla adı olan dosyalarla ilgilidir.

DirectAdmin, bir kullanıcının yedeğini almadan önce arşive girecek verileri tarar ve sabit bağlantı bulursa o kullanıcının yedeğini iptal eder. Bütün yöneticilere bildirim gider, kullanıcının arşivi oluşmaz ve neden ortadan kalkana kadar her zamanlanmış yedek aynı şekilde başarısız olur. Kendi sunucularımızda aşağıdaki sırayı izliyoruz.

Kısa cevap​

Önce bul, sonra düzelt, en son karar ver.
  1. Dosyaları listeleyin: find /home/KULLANICI -xdev -type f -links +1 -ls
  2. Kaynağı düzeltin: Dovecot e-postaları varsayılan olarak sabit bağlantıyla kopyalar (maildir_copy_with_hardlinks = no bunu durdurur), bazı Node.js ve Python araçları önbellekteki dosyalara sabit bağlantı oluşturur, anlık görüntülerin (snapshot) yeri de /home dışıdır.
  3. Var olan bağlantıları ayırın: her dosyayı kopyalayın, sonra kopyayı asıl adın üzerine taşıyın.
  4. Kontrolü (backup_hard_link_check=0) yalnızca strict_backup_permissions=1 ve fs.protected_hardlinks=1 etkinse ve hiçbir kullanıcının ev dizininde sahibi olmadığı bir dosyaya bağlantı yoksa kapatın.
  5. Deneme yedeği ve geri yükleme testiyle bitirin.

Hata mesajı ne söylüyor?​

DirectAdmin'in güncel belgelerinde mesaj şöyle geçiyor:
Code:
Backup Abort (2): A hard link was found under User username's path
Bildirimin konu satırındaki parantezli sayı, bağlantı bulunduğu sırada yedeğin hangi bölümünün işlendiğini gösterir:
SayıBölüm (DirectAdmin belgeleri)İlk bakılacak yer
1~/MaildirSistem hesabının kendi posta kutusu
2~/imapHesaptaki alan adlarının posta kutuları (belgelere göre buradaki bağlantılar meşru da olabilir)
3Yedek başlamadan önceki hazırlama alanı (assembly area)backup_tmpdir ile belirlenen geçici dizin (varsayılan /home/tmp); orada bir şey yoksa ev dizininin tamamı
4~/domainsWeb dosyaları: node_modules, sanal ortamlar, snapshot klasörleri
Konunun başlığı, ilk vakadaki genel özet satırını aktarıyor: "An error occurred during the backup". Bu satır nedeni söylemez. Ayrıntı için yönetici panelindeki mesaj kutusunu (Message System) açın; DirectAdmin bu bildirimi, yedeği kullanıcı kendi panelinden başlatmış olsa bile her yöneticiye gönderir. DirectAdmin belgelerindeki kaynaklar: Backup Abort (2) ve backup_hard_link_check.

Sabit bağlantı mı, sembolik bağlantı mı?​

ext4, XFS ve benzeri Linux dosya sistemlerinde bir dosyanın üst bilgileri (sahibi, izinleri, boyutu) ve verisinin diskteki yeri inode adı verilen kayıtta tutulur; dizindeki dosya adı yalnızca bu kaydı gösterir. Sabit bağlantı, aynı inode'a verilmiş ikinci bir addır: iki ad eşittir, hiçbiri "asıl dosya" değildir ve son ad silinene kadar veri diskte kalır. ls -l ve stat çıktısındaki bağlantı sayısı (link count), inode'un kaç adı olduğunu gösterir. Sembolik bağlantı (symlink) ise içinde yalnızca bir yol yazan ayrı, küçük bir dosyadır.
Sabit bağlantıSembolik bağlantı
Nedir?Aynı inode'un başka bir adıİçinde yol yazan ayrı bir dosya
Sahip ve izinlerBütün adlarda ortakHedef dosyada denetlenir
Dosya sistemleri arasındaMümkün değilMümkün
tar arşivindeİçerik bir kez, diğer adlar için bağlantı kaydıHedef değil, bağlantının kendisi
Bu hatayı tetikler mi?EvetHayır

DirectAdmin yedeği neden durduruyor?​

Bağlantı koruması olmayan bir sistemde kullanıcı, aynı dosya sisteminde bulunan ve sahibi olmadığı bir dosyaya kendi ev dizininde sabit bağlantı oluşturabilir; DirectAdmin belgeleri örnek olarak /etc/shadow dosyasını veriyor. Bağlantı asıl dosyanın sahibini ve izinlerini taşıdığı için kullanıcı dosyayı yine okuyamaz. Tehlike, kullanıcının dizinini yetkili bir sürecin okumasıdır: root yetkisiyle çalışan bir yedekleme, bu dosyayı kullanıcının indirebileceği bir arşive koyardı. DirectAdmin yedek öncesi kontrolü bu nedenle 1.35.1 sürümünde (Şubat 2010) ekledi.

Bugün aynı açığı iki katman daha kapatıyor:
  • strict_backup_permissions=1 DirectAdmin'in güncel belgelerine göre varsayılan değerdir; arşiv kullanıcı yetkisiyle oluşturulur ve kullanıcı okuyamadığı dosyayı arşive de koyamaz. Seçenek 2010'da eklendiğinde varsayılanı 0'dı; eski bir directadmin.conf dosyasında hâlâ 0 yazıyor olabilir.
  • fs.protected_hardlinks=1 olduğunda çekirdek, kullanıcı kaynak dosyanın sahibi değilse ve dosya üzerinde okuma/yazma izni de yoksa sabit bağlantı oluşturmayı reddeder (çekirdek belgeleri). systemd'nin varsayılan sysctl dosyası bu değeri 1 yaptığı için çoğu güncel dağıtımda açıktır; yine de kontrol edin.
Dovecot'un, esbuild'in, pnpm'in ve benzeri araçların oluşturduğu bağlantılar kullanıcının kendi dosyalarını gösterir ve böyle bir risk taşımaz. Kontrol bu farkı göremez; hiçbir sorunu olmayan sıradan sunucularda da bu hatanın çıkmasının nedeni budur.

1. Adım: Sabit bağlantıları bulun​

Komutları root olarak çalıştırın; fred yerine bildirimde adı geçen hesabı yazın.
Bash:
find /home/fred -xdev -type f -links +1 -ls
-xdev aramayı tek dosya sisteminde tutar (sabit bağlantılar zaten dosya sistemi sınırını aşamaz), -type f ext4 ve XFS'te en az iki bağlantısı olan dizinleri dışarıda bırakır, -links +1 ise "birden fazla bağlantı" demektir. Kısaltılmış örnek çıktı:
Code:
1442211  9876 -rwxr-xr-x   2 fred  fred  10112345 Oct 20 09:14 /home/fred/domains/example.com/app/node_modules/esbuild/bin/esbuild
1442211  9876 -rwxr-xr-x   2 fred  fred  10112345 Oct 20 09:14 /home/fred/domains/example.com/app/node_modules/@esbuild/linux-x64/bin/esbuild
Sütunlar sırasıyla inode numarası, 1 KiB'lık blok cinsinden boyut, izinler, bağlantı sayısı, sahip, grup, bayt cinsinden boyut, değişiklik zamanı ve yoldur. İki satırda aynı inode numarasının görünmesi, iki ayrı adı olan tek bir dosya demektir; dördüncü sütundaki 2 de bunu doğrular. Bağlantı sayısı 2 olduğu hâlde tek satır görüyorsanız diğer ad, aradığınız dizinin dışındadır.
Bash:
# en çok bağlantının bulunduğu dizinler genellikle kaynağı ele verir
find /home/fred -xdev -type f -links +1 -printf '%h\n' | sort | uniq -c | sort -rn | head -20

# bir dosyanın bütün adları (/home ayrı bir bölüm değilse / altında arayın)
find /home -xdev -samefile /home/fred/dosya/yolu
stat -c '%h bağlantı, inode %i, sahip %U: %n' /home/fred/dosya/yolu

# güvenlik kontrolü: fred'in ev dizininde, sahibi fred olmayan bağlantılı dosyalar
find /home/fred -xdev -type f -links +1 ! -user fred -ls
Son komut hiçbir şey yazdırmamalı. Bir kullanıcının ev dizininde root'a ya da başka bir hesaba ait bağlantılı bir dosya varsa bu bir yedekleme sorunu değil, güvenlik olayıdır: herhangi bir ayarı değiştirmeden önce dosyayı kimin, neden oluşturduğunu bulun.

2. Adım: Kaynağı belirleyin​

YerOlası nedenKalıcı çözüm
~/imap/…/Maildir, ~/MaildirDovecot iletileri sabit bağlantıyla kopyalar (maildir_copy_with_hardlinks varsayılan olarak yes): e-posta istemcisinin iletileri klasörler arasında kopyalaması (IMAP COPY), tek bir iletinin aynı hesaptaki birden çok posta kutusuna LMTP ile teslimimaildir_copy_with_hardlinks = no, ardından var olan bağlantıları ayırmak
node_modules/esbuild/bin/esbuild ve platform paketindeki ikiziesbuild kurulum betiği, JavaScript başlatıcısını yerel çalıştırılabilir dosyaya açılan bir sabit bağlantıyla değiştirir (Windows dışındaki sistemlerde; Yarn ya da --ignore-scripts kullanılmadığında)Derlemeyi başka yerde yapıp yalnızca çıktıyı yüklemek ya da her kurulumdan sonra bağlantıyı ayırmak
Diğer adı ~/.local/share/pnpm/store ya da ~/.cache/uv altında olan node_modules veya .venv dosyalarıpnpm'in varsayılan içe aktarma yöntemi Linux'ta önce sabit bağlantıyı dener; uv ise ext4 gibi klonlamayı desteklemeyen dosya sistemlerinde klonlamadan sabit bağlantıya geçerAracı kopyalama moduna alıp yeniden kurmak
Snapshot klasörleri (daily.0, daily.1 … ya da tarih adlı klasörler)rsync --link-dest ya da cp -al, değişmeyen dosyaları snapshot'lar arasında paylaştırırSnapshot'ları /home dışındaki bir diskte ya da sunucuda tutmak
Başka bir sunucunun arşivinden geri yüklenen e-posta ya da dosyalartar, arşivde kayıtlı sabit bağlantıları yeniden oluştururHer taşımadan sonra tarayıp bağlantıları ayırmak
E-posta bağlantıları normalde tek bir hesabın içinde kalır. Dovecot'un kodu, ileti dosyaları yalnızca sahibine açıksa (0600 izni) farklı sahipler arasında sabit bağlantı kurmaz. LMTP kodu ise aynı teslimattaki sonraki alıcılar için ilk kaydedilen kopyayı yeniden kullanır; kaynak koddaki açıklama bunun nedenini "this might allow hard linking the files" (dosyaların sabit bağlantıyla paylaşılabilmesi) olarak veriyor. Posta Dovecot'a LMTP ile teslim ediliyorsa, aynı alan adındaki info@ ve satis@ adreslerine tek oturumda giden bir ileti bu yüzden iki adı olan tek bir inode'a dönüşebilir.

3. Adım: Bu sırayla düzeltin​

3.1 Var olan bağlantıları ayırın: önce kopyala, sonra taşı​

Aynı üst bilgilere sahip bağımsız bir kopya oluşturun ve bu kopyayı asıl adın üzerine yeniden adlandırın. Aynı dosya sistemi içindeki yeniden adlandırma atomiktir; dosyayı okuyan program yarım yazılmış bir kopya görmez. Diğer ad eski inode'u göstermeye devam eder, inode'un bağlantı sayısı ise bir azalır. Bunun yerine adlardan birini silmeyin: Maildir'de iki ad da kullanıcının farklı klasörlerde gördüğü iletilerdir.

Başlamadan önce, ayrılan her bağlantının tam bir kopyaya dönüştüğünü unutmayın. Aşağıdaki komutun verdiği tahmini (bir üst sınırdır) hesabın kotasıyla karşılaştırın; root olarak çalışan bir işlem hesabı kotasının üzerine çıkarabilir ve bu, e-posta teslimini durdurabilir. Hesabın güncel bir kopyasının başka bir yerde bulunduğundan emin olun; posta kutularında önce 3.2'yi uygulayın ve trafiğin az olduğu bir saati seçin.
Bash:
find /home/fred/imap -xdev -type f -links +1 -printf '%s\n' | awk '{s+=$1} END {printf "%.0f MiB\n", s/1048576}'
Betik, --apply eklemediğiniz sürece yalnızca listeler. Geçici kopya aynı dizinde durur, böylece mv atomik bir yeniden adlandırma olarak kalır; kopyanın adı noktayla başlar, çünkü Dovecot, Maildir klasörlerinde adı noktayla başlayan dosyaları yok sayar ve kopyayı yeni bir ileti sanmaz.
Bash:
#!/bin/bash
# break-hardlinks.sh DIR [--apply]
set -u
dir="${1:-}"; mode="${2:-}"
[ -d "$dir" ] || { echo "usage: $0 DIR [--apply]" >&2; exit 1; }
find "$dir" -xdev -type f -links +1 -print0 |
while IFS= read -r -d '' f; do
  [ -f "$f" ] && [ "$(stat -c %h -- "$f")" -gt 1 ] || continue
  if [ "$mode" != "--apply" ]; then echo "would split: $f"; continue; fi
  tmp="$(dirname -- "$f")/.split.$$.$(basename -- "$f")"
  if cp -p -- "$f" "$tmp" && mv -f -- "$tmp" "$f"; then
    echo "split: $f"
  else
    echo "FAILED: $f" >&2; rm -f -- "$tmp"
  fi
done
Bash:
bash break-hardlinks.sh /home/fred/imap             # yalnızca listele
bash break-hardlinks.sh /home/fred/imap --apply     # ayır
find /home/fred -xdev -type f -links +1 | wc -l     # başka kaynak kalmadıysa 0
cp -p izinleri ve zaman damgalarını, root olarak çalıştığında sahip ve grubu da korur. Gerçek çalıştırma, eşi zaten ayrılmış adları atladığı için deneme çalıştırmasının (dry run) listelediğinden daha az kopya üretir; bunu adında boşluk, iki nokta ve virgül geçen test dosyalarında denedik. Dovecot bir iletiyi kopyalama ile taşıma arasında yeniden adlandırırsa (örneğin bir bayrak değişikliğinde) ileti iki kez görünebilir. Veri kaybolmaz, fazla kopyayı silmek yeterlidir; sakin bir saatte bu pek olası da değildir.

3.2 Dovecot: e-postayı sabit bağlantıyla kopyalamayı kapatın​

maildir_copy_with_hardlinks, Dovecot 2.3'te de 2.4'te de varsayılan olarak yes değerindedir ve belgeler bunu "unlikely to have any side effects", yani yan etkisi pek beklenmeyen bir performans kazancı olarak tanımlar. DirectAdmin'in yedeği için ise bir yan etkisi var. Değişikliği, oluşturulan dovecot.conf dosyasını düzenlemek yerine DirectAdmin belgelerinin diğer Dovecot ayarlarında yaptığı gibi ayrı bir dosyaya yazın:
Bash:
echo 'maildir_copy_with_hardlinks = no' > /etc/dovecot/conf.d/90-maildir-copy.conf
doveconf maildir_copy_with_hardlinks        # beklenen: maildir_copy_with_hardlinks = no
systemctl restart dovecot
Yalnızca yeni kopyalar etkilenir. Var olan bağlantılar siz ayırana kadar kalır ve kopyalanan iletiler artık gerçekten disk alanı kaplar. Dovecot yapılandırmasını yeniden oluşturan bir CustomBuild güncellemesinden sonra (SSH üzerinden CustomBuild güncelleme rehberimiz) doveconf komutunu yeniden çalıştırın.

3.3 Snapshot klasörlerini /home dışına taşıyın​

rsync --link-dest ile alınan snapshot'lar, değişmeyen dosyaları paylaştıkları için az yer kaplar. Hesabın içinde bu, binlerce sabit bağlantı ve fazladan kota kullanımı demektir; snapshot'ları ayrı bir diske ya da yedek sunucusuna yazın:
Bash:
bugun=$(date +%F)
mkdir -p /backup/fred
rsync -a --link-dest=/backup/fred/latest /home/fred/domains/ "/backup/fred/$bugun/"
ln -sfn "/backup/fred/$bugun" /backup/fred/latest
Buradaki /backup, /home dışında bir yere bağlanmış (mount) ayrı bir disktir. İlk çalıştırmada /backup/fred/latest henüz bulunmadığı için rsync bir uyarı verir ve tam kopya alır. Ev dizininin en üst düzeyinde kalması gereken bir snapshot klasörü, skip_backup_home_files.list ile (kullanıcıya özel dosya /usr/local/directadmin/data/users/KULLANICI/ altında) arşiv dışında bırakılabilir; belgeler bunun klasörü sabit bağlantı kontrolünden de muaf tutup tutmadığını söylemiyor, deneyerek doğrulayın.

3.4 Node.js, Python ve git​

  • esbuild: Derlemeyi kendi bilgisayarınızda ya da CI'da yapıp sunucuya yalnızca çıktıyı yükleyin. Derleme sunucuda yapılmak zorundaysa her kurulumdan sonra bağlantıyı yukarıdaki betikle ayırın. npm ci --ignore-scripts de bağlantıyı önler ama bütün paketlerin kurulum betiklerini atlar.
  • pnpm: pnpm-workspace.yaml ya da genel config.yaml dosyasına packageImportMethod: copy yazın (eski sürümler bu ayarı .npmrc içinde package-import-method=copy olarak okur), ardından node_modules klasörünü silip yeniden kurun.
  • uv: pyproject.toml dosyasının tool.uv tablosuna link-mode = "copy" ekleyin ya da UV_LINK_MODE=copy değişkenini tanımlayın; sonra .venv klasörünü silip uv sync çalıştırın.
  • git: Yerel bir yoldan klonlama, .git/objects dosyalarını sabit bağlantıyla oluşturur; --no-hardlinks ekleyin.

4. Adım: backup_hard_link_check kararını en sona bırakın​

DirectAdmin belgeleri kontrolün "as long as you keep strict_backup_permissions=1", yani strict_backup_permissions=1 korunduğu sürece kapatılabileceğini söylüyor; kontrol yedekler sırasında fazla yük bindiriyorsa ve bütün kullanıcılarınıza güveniyorsanız kapatılabileceğini de belirtiyor. Seçeneği getiren 2010 tarihli sürüm notu ise "It's recommended to leave it on" diyerek açık bırakılmasını öneriyor. Biz bu koşula iki koşul daha ekliyoruz: çekirdeğin bağlantı koruması ve temiz bir sahiplik denetimi. Üçünü de kontrol edin:
Bash:
sysctl fs.protected_hardlinks                                     # 1 olmalı
da config | grep -E 'strict_backup_permissions|backup_hard_link_check'
for d in /home/*/; do u=$(basename "$d"); id -u "$u" >/dev/null 2>&1 || continue
  nice -n 19 find "$d" -xdev -type f -links +1 ! -user "$u" -printf "$u: %u %p\n"
done                                                              # çıktı olmamalı
da, /usr/local/directadmin/directadmin için kısayoldur; da config yalnızca dosyaya yazılmış değerleri değil, yerleşik varsayılanları da gösterir. fs.protected_hardlinks 0 çıkarsa önce değeri 1 yapıp kalıcı hâle getirin:
Bash:
echo 'fs.protected_hardlinks = 1' > /etc/sysctl.d/90-protected-hardlinks.conf
sysctl --system
Üç koşul da sağlanınca dosyayı düzenleyip servisi yeniden başlatın ya da config-set kullanın:
Bash:
# A: /usr/local/directadmin/conf/directadmin.conf içinde
#    strict_backup_permissions=1
#    backup_hard_link_check=0
systemctl restart directadmin

# B: komut satırından
da config-set strict_backup_permissions 1
da config-set backup_hard_link_check 0
systemctl restart directadmin   # v1.710'dan beri config-set DirectAdmin'i kendisi yeniden yükler; eski sürümlerde bu satır ya da --restart gerekir
Belgelerin aynı bölümü, ~/imap altındaki bağlantıların meşru olabileceğini belirterek direct_imap_backup=1 ayarını da öneriyor; bu ayar imap klasörünü doğrudan arşive yazar ve yedeği hızlandırır. Yapılandırma başvuru sayfası bu ayarın varsayılanını 1 olarak gösteriyor; sizdeki değeri da config ile kontrol edin.
DurumKarar
Bağlantılar ~/imap altındaydı; Dovecot ayarı değişti, bağlantılar ayrıldıKontrol açık kalsın
Bağlantılar değiştiremediğiniz bir yazılımdan gelmeye devam ediyor ve üç koşul da sağlanıyorKapatmak kabul edilebilir
Eski bir yapılandırmada strict_backup_permissions=0Kapatmayın; önce 1 yapıp deneme yedeği alın
Bir ev dizininde kullanıcının sahibi olmadığı bağlantılı bir dosya var ya da müşterilerinizi tanımıyorsunuzAçık kalsın; araştırın ve kaynakları düzeltin
Kontrolü kapatırsanız alarmın yerine başka bir denetim koyun: yukarıdaki döngüyü başına #!/bin/bash satırı ekleyerek /usr/local/sbin/hardlink-audit.sh dosyasına kaydedin ve haftalık çalıştırın. Döngü normalde yalnızca bir bulgu olduğunda çıktı üretir; cron da her çıktıyı genellikle root'a e-postayla gönderir.
Bash:
chmod 700 /usr/local/sbin/hardlink-audit.sh
echo '30 3 * * 1 root /usr/local/sbin/hardlink-audit.sh' > /etc/cron.d/hardlink-audit

5. Adım: Deneme yedeği, ardından geri yükleme testi​

Tamamlanan bir yedek, geri yüklenebilen bir yedek demek değildir. Etkilenen hesabın yedeğini Admin Backup/Transfer (yönetici), Manage User Backups (bayi) ya da Site Backup (kullanıcı) ekranından veya komut satırından oluşturun:
Bash:
/usr/local/directadmin/directadmin admin-backup --destination=/home/admin/admin_backups --user=fred
ls -lh /home/admin/admin_backups/ | grep fred
Yedeği inceleyin: yeni bir Backup Abort bildirimi gelmemeli ve arşiv beklediğiniz bölümleri içermeli. Tam dosya adını ls çıktısından alın: adın ortasındaki bölüm her zaman admin olmayabilir, backup_gzip=2 ayarlıysa uzantı da .tar.gz yerine .tar.zst olur.
Bash:
tar -tzf /home/admin/admin_backups/user.admin.fred.tar.gz | less
zstd -dc /home/admin/admin_backups/user.admin.fred.tar.zst | tar -tf - | less   # .tar.zst yedekler için
Arşivi canlı hesabın üzerine değil, ayrı bir test sunucusuna geri yükleyin. Siteyi hosts dosyasına eklediğiniz bir kayıtla açın, veritabanı tablolarını kontrol edin ve iki sunucudaki posta kutusu toplamlarını karşılaştırın:
Bash:
doveadm mailbox status -u [email protected] -t messages '*'
Geri yüklemenin ne kadar sürdüğünü not edin; gerçek kurtarma süreniz budur. 3-2-1 kuralını ve geri yükleme testlerinin takvimini gerçekten geri yüklenen yedekler rehberimizde anlattık. Hesapları cPanel'den taşıdıysanız (cPanel'den DirectAdmin'e transfer rehberi) ilk zamanlanmış yedekten önce sabit bağlantı taraması yapın.

Sık yapılan hatalar​

  • Önce kontrolü kapatıp nedene hiç bakmamak. Başkasına ait bir dosyaya açılmış bağlantının uyarısı da onunla birlikte kaybolur.
  • Bağlantıdan kurtulmak için adlardan birini silmek. Maildir'de bu, iletiyi bir klasörden siler.
  • -p olmadan kopyalamak. Zaman damgaları, izinler ve root olarak çalışırken sahiplik değişir.
  • Geçici kopyayı /tmp altına koymak. Dosya sistemleri arası taşıma atomik bir yeniden adlandırma değildir.
  • Büyük bir posta kutusunda kotaya bakmadan bağlantıları ayırmak.
  • İzin hatalarını susturmak için strict_backup_permissions=0 yapmak. Yedek bu durumda dosyaları daha yüksek yetkiyle okur; kontrolün var olma nedeni tam da budur.

Sık sorulan sorular​

Bu hatanın disk kotasındaki sabit limitle ilgisi var mı?
Hayır. Sabit bağlantı (hard link) birden fazla adı olan dosyadır; sabit limit (hard limit) ise kotanın üst sınırıdır. Kota yalnızca bağlantıları ayırırken önem kazanır, çünkü her ayırma tam bir kopya demektir.

Posta kutusundaki sabit bağlantılar tehlikeli mi?
Dosya kullanıcının kendisine aitse hayır; Dovecot'un oluşturduğu bağlantılar böyledir. Kontrol zararsızı kötü niyetliden ayıramadığı için sıradan sunucularda da devreye girer.

maildir_copy_with_hardlinks = no daha fazla disk alanı kullanır mı?
Evet, yeni kopyalarda. Var olan bağlantılar siz ayırana kadar olduğu gibi kalır.

tar, sabit bağlantılı bir dosyayı iki kez saklar mı?
Hayır. GNU tar içeriği bir kez saklar, diğer adlar için bağlantı kaydı ekler ve arşivi açarken bağlantıları yeniden kurar. Yedeğin durması tar'ın bir sınırı değil, DirectAdmin'in güvenlik politikasıdır.

Sembolik bağlantı bu hataya yol açar mı?
Hayır. DirectAdmin belgelerine göre sembolik bağlantılar sorun değildir, çünkü tar hedef dosyayı değil bağlantının kendisini ekler.

Kontrolü kapattık. Şimdi ne yapmalıyız?
strict_backup_permissions=1 ve fs.protected_hardlinks=1 ayarlarını koruyun, haftalık denetimi çalıştırın ve geri yükleme testlerini sürdürün.

Sonuç ve sonraki adımlar​

Bu kontrol kaba bir alarmdır: Dovecot'un zararsız kopyalarını /etc/shadow dosyasına açılmış bir bağlantıdan ayıramaz, bu yüzden birinin bakması gerekir. Kaynak sık sık ~/imap altındadır; o durumda tek bir Dovecot ayarı ve bir defalık temizlik, kontrol açık kalırken hataları bitirir.
  1. Bildirimde adı geçen hesap için find komutunu çalıştırın ve kaynağı belirleyin.
  2. Kaynağı düzeltin: Dovecot ayarı, derleme süreci, snapshot konumu.
  3. Kalan bağlantıları kopyala-taşı betiğiyle ayırın.
  4. Kontrolü yalnızca strict_backup_permissions=1, fs.protected_hardlinks=1 ve temiz bir denetim sonucu bir aradaysa kapatın.
  5. Bir yedek alıp geri yükleyin, ardından geri yükleme testini düzenli olarak tekrarlayın.
Bu denetimi ve geri yükleme testlerini başkasının üstlenmesini isterseniz AIOR ekibi bunları Linux sunucu yönetimi hizmeti kapsamında yapıyor. Burada yer almayan bir mesaj türüyle karşılaşırsanız tam metnini bu konuya yazın.

DirectAdmin stops a user's backup when it finds a hard link in the data it is about to archive. Every admin gets a notice, that user's archive is not created, and each scheduled run fails the same way until the cause is gone. We handle it on our own servers in the order below. To be clear from the start: this is about hard links (files with more than one name), not about the hard limit of a disk quota.

Short answer​

Find, fix, then decide.
  1. List the files: find /home/USER -xdev -type f -links +1 -ls
  2. Fix the source: Dovecot copies mail with hard links by default (maildir_copy_with_hardlinks = no stops it), some Node.js and Python tools hard link files from a cache, and snapshots belong outside /home.
  3. Split the links that already exist: copy each file, then move the copy over the original name.
  4. Disable the check (backup_hard_link_check=0) only with strict_backup_permissions=1, fs.protected_hardlinks=1 and no links to files the user does not own.
  5. Finish with a test backup and a test restore.

What the message says​

DirectAdmin's current documentation gives the message as:
Code:
Backup Abort (2): A hard link was found under User username's path
The number in parentheses, part of the notice's subject line, shows which part of the backup was running when the link turned up:
NumberArea (DirectAdmin docs)Look first at
1~/MaildirThe system account's own mailbox
2~/imapMailboxes of the account's domains (the docs note that links here can be legitimate)
3Backup assembly area, before the backup startsThe staging directory set by backup_tmpdir (default /home/tmp); if that is clean, the whole home directory
4~/domainsWebsite files: node_modules, virtual environments, snapshots
This thread's title quotes the generic summary from the original case, "An error occurred during the backup", which does not name the cause. Open the full notice in the admin Message System; DirectAdmin sends it to every admin, even when the user started the backup from their own panel. References in the DirectAdmin docs: Backup Abort (2) and backup_hard_link_check.

Hard link or symbolic link?​

On ext4, XFS and similar Linux filesystems, a file's metadata (owner, permissions, size) and the location of its data live in an inode, and a name in a directory just points to it. A hard link is a second name for the same inode: both names are equal, neither is "the original", and the data stays on disk until the last name is deleted. The link count in ls -l and stat output is the number of names. A symbolic link is different: a separate small file that stores a path.
Hard linkSymbolic link
What it isAnother name for the same inodeA separate file containing a path
Owner and permissionsShared by all namesChecked on the target
Across filesystemsNot possiblePossible
In a tar archiveContent once, link entries for the other namesThe link itself, not the target
Triggers this abortYesNo

Why DirectAdmin aborts​

On a system without link protection, a user can hard link a file they do not own into their home directory, provided it sits on the same filesystem; DirectAdmin's documentation uses /etc/shadow as the example. The link keeps the original owner and permissions, so the user still cannot read the file. The danger is a privileged process reading the user's directory: a backup running with root rights would put that file into an archive the user can download. DirectAdmin added the pre-backup check in version 1.35.1 (February 2010) for this reason.

Two other layers close the same hole today:
  • strict_backup_permissions=1, the default in DirectAdmin's current documentation, builds the archive as the user, who cannot read such a file. Its default was 0 when it was introduced in 2010, so an old directadmin.conf may still say 0.
  • fs.protected_hardlinks=1 makes the kernel refuse a hard link unless the user owns the source file or has read/write access to it (kernel documentation). Upstream systemd ships it as 1, so most current distributions have it on. Check anyway.
Links created by Dovecot, esbuild, pnpm and similar tools point to the user's own files and carry no such risk. The check cannot tell the difference, which is why healthy, ordinary servers trip it too.

Step 1: Find the hard links​

Run as root and replace fred with the account named in the notice.
Bash:
find /home/fred -xdev -type f -links +1 -ls
-xdev keeps find on one filesystem (hard links cannot cross filesystems anyway), -type f skips directories, which have two or more links on ext4 and XFS, and -links +1 means more than one link. Example output, shortened:
Code:
1442211  9876 -rwxr-xr-x   2 fred  fred  10112345 Oct 20 09:14 /home/fred/domains/example.com/app/node_modules/esbuild/bin/esbuild
1442211  9876 -rwxr-xr-x   2 fred  fred  10112345 Oct 20 09:14 /home/fred/domains/example.com/app/node_modules/@esbuild/linux-x64/bin/esbuild
The columns are inode number, size in 1 KiB blocks, permissions, link count, owner, group, size in bytes, modification time and path. The same inode number on two lines means one file under two names, and the 2 in the fourth column confirms it. A count of 2 with only one line means the other name is outside the directory you searched.
Bash:
# directories with the most links usually reveal the source
find /home/fred -xdev -type f -links +1 -printf '%h\n' | sort | uniq -c | sort -rn | head -20

# all names of one file (search from / if /home is not a separate mount)
find /home -xdev -samefile /home/fred/path/to/file
stat -c '%h links, inode %i, owner %U: %n' /home/fred/path/to/file

# security check: linked files in fred's home that fred does not own
find /home/fred -xdev -type f -links +1 ! -user fred -ls
The last command should print nothing. A linked file owned by root or by another account inside a user's home is a security incident, not a backup problem: find out who created it and why before you change any setting.

Step 2: Identify the source​

WhereTypical causeLasting fix
~/imap/…/Maildir, ~/MaildirDovecot copies messages with hard links (maildir_copy_with_hardlinks is yes by default): a mail client copying messages between folders (IMAP COPY), LMTP delivery of one message to several mailboxes of one accountmaildir_copy_with_hardlinks = no, then split the existing links
node_modules/esbuild/bin/esbuild and its twin in the platform packageesbuild's install script swaps its JavaScript launcher for a hard link to the native binary (on every OS except Windows, unless Yarn or --ignore-scripts is used)Build elsewhere and deploy the output, or split the link after each install
node_modules or .venv files whose other name is in ~/.local/share/pnpm/store or ~/.cache/uvpnpm's default import method tries hard links first on Linux; uv falls back from cloning to hard links on filesystems such as ext4Switch the tool to copy mode and reinstall
Snapshot folders (daily.0, daily.1 … or dated names)rsync --link-dest or cp -al share unchanged files between snapshotsKeep snapshots on a disk or server outside /home
Mail or files restored from another server's archivetar recreates the hard links stored in the archiveScan after every migration and split the links
Mail links normally stay inside one account. Dovecot's code will not hard link between different owners when message files are private (mode 0600), and its LMTP code reuses the first saved copy for further recipients of the same delivery because, as the source comment puts it, "this might allow hard linking the files". If mail reaches Dovecot over LMTP, one message delivered to info@ and sales@ of the same domain in a single session can therefore become one inode with two names.

Step 3: Fix, in this order​

3.1 Split the existing links: copy, then move​

Make an independent copy with the same metadata, then rename it over the original name. A rename within one filesystem is atomic, so a program reading the file never sees a half-written copy. The other name keeps pointing to the old inode, whose link count drops by one. Do not delete one of the names instead: in a Maildir both are messages the user sees, in different folders.

Before you start, remember that every split becomes a full copy. Compare the estimate from the command below (an upper bound) with the account's quota: a process running as root can push the account over its quota, and that can stop its mail delivery. Make sure a recent copy of the account exists elsewhere, and for mailboxes apply 3.2 first and pick a quiet hour.
Bash:
find /home/fred/imap -xdev -type f -links +1 -printf '%s\n' | awk '{s+=$1} END {printf "%.0f MiB\n", s/1048576}'
The script only lists files until you add --apply. The temporary copy sits in the same directory, so mv stays an atomic rename, and its name starts with a dot, because Dovecot ignores files whose names start with a dot in Maildir folders and will not take the copy for a new message.
Bash:
#!/bin/bash
# break-hardlinks.sh DIR [--apply]
set -u
dir="${1:-}"; mode="${2:-}"
[ -d "$dir" ] || { echo "usage: $0 DIR [--apply]" >&2; exit 1; }
find "$dir" -xdev -type f -links +1 -print0 |
while IFS= read -r -d '' f; do
  [ -f "$f" ] && [ "$(stat -c %h -- "$f")" -gt 1 ] || continue
  if [ "$mode" != "--apply" ]; then echo "would split: $f"; continue; fi
  tmp="$(dirname -- "$f")/.split.$$.$(basename -- "$f")"
  if cp -p -- "$f" "$tmp" && mv -f -- "$tmp" "$f"; then
    echo "split: $f"
  else
    echo "FAILED: $f" >&2; rm -f -- "$tmp"
  fi
done
Bash:
bash break-hardlinks.sh /home/fred/imap             # list only
bash break-hardlinks.sh /home/fred/imap --apply     # split
find /home/fred -xdev -type f -links +1 | wc -l     # expect 0 unless other sources remain
cp -p keeps permissions and timestamps, and as root also owner and group. The real run skips a name once its partner has been split, so it makes fewer copies than the dry run lists; we checked this on test files with spaces, colons and commas in their names. If Dovecot renames a message (on a flag change) between the copy and the move, the message can show up twice. No data is lost and deleting the extra copy is enough; in a quiet hour it is also unlikely.

3.2 Dovecot: stop copying mail with hard links​

maildir_copy_with_hardlinks defaults to yes in Dovecot 2.3 and 2.4, and the documentation calls it a performance gain "unlikely to have any side effects". For DirectAdmin's backup it does have one. Put the change in its own file, as DirectAdmin's docs do for other Dovecot tweaks, rather than editing the generated dovecot.conf:
Bash:
echo 'maildir_copy_with_hardlinks = no' > /etc/dovecot/conf.d/90-maildir-copy.conf
doveconf maildir_copy_with_hardlinks        # expect: maildir_copy_with_hardlinks = no
systemctl restart dovecot
Only new copies are affected. Existing links stay until you split them, and copied messages now use real disk space. After a CustomBuild update that rebuilds the Dovecot configuration (see our CustomBuild over SSH guide), run doveconf again.

3.3 Snapshots: move them out of /home​

Snapshots made with rsync --link-dest are cheap precisely because unchanged files are shared. Inside an account that means thousands of hard links and extra quota use, so write them to a separate disk or a backup server instead:
Bash:
today=$(date +%F)
mkdir -p /backup/fred
rsync -a --link-dest=/backup/fred/latest /home/fred/domains/ "/backup/fred/$today/"
ln -sfn "/backup/fred/$today" /backup/fred/latest
Here /backup is a separate disk mounted outside /home. On the first run /backup/fred/latest does not exist yet, so rsync prints a warning and makes a full copy. A snapshot folder that must stay at the top level of a home directory can be kept out of the archive with skip_backup_home_files.list (per user in /usr/local/directadmin/data/users/USERNAME/); the docs do not say whether that also exempts it from the hard link check, so test.

3.4 Node.js, Python and git​

  • esbuild: build on a workstation or in CI and upload only the output. If you must build on the server, split the link after each install with the script above. npm ci --ignore-scripts also avoids it, but skips every package's install scripts.
  • pnpm: set packageImportMethod: copy in pnpm-workspace.yaml or the global config.yaml (older versions read package-import-method=copy from .npmrc), then delete node_modules and reinstall.
  • uv: set link-mode = "copy" in the tool.uv table of pyproject.toml, or export UV_LINK_MODE=copy, then delete .venv and run uv sync.
  • git: cloning from a local path hard links .git/objects; add --no-hardlinks.

Step 4: Only then decide about backup_hard_link_check​

DirectAdmin's documentation allows disabling the check "as long as you keep strict_backup_permissions=1", and mentions doing so when the check adds too much load during backups and you trust all your users. The 2010 release note that introduced the option adds: "It's recommended to leave it on." We add two conditions of our own: the kernel's link protection and a clean ownership audit. Check all three:
Bash:
sysctl fs.protected_hardlinks                                     # want 1
da config | grep -E 'strict_backup_permissions|backup_hard_link_check'
for d in /home/*/; do u=$(basename "$d"); id -u "$u" >/dev/null 2>&1 || continue
  nice -n 19 find "$d" -xdev -type f -links +1 ! -user "$u" -printf "$u: %u %p\n"
done                                                              # want no output
da is the shortcut for /usr/local/directadmin/directadmin, and da config shows the built-in defaults as well as the values written in the file. If fs.protected_hardlinks is 0, set it to 1 and make it persistent first:
Bash:
echo 'fs.protected_hardlinks = 1' > /etc/sysctl.d/90-protected-hardlinks.conf
sysctl --system
When all three hold, edit the file and restart, or use config-set:
Bash:
# A: in /usr/local/directadmin/conf/directadmin.conf
#    strict_backup_permissions=1
#    backup_hard_link_check=0
systemctl restart directadmin

# B: from the command line
da config-set strict_backup_permissions 1
da config-set backup_hard_link_check 0
systemctl restart directadmin   # since v1.710 config-set reloads DirectAdmin by itself; older versions need this or --restart
The same documentation section notes that hard links under ~/imap can be legitimate and recommends direct_imap_backup=1, which writes the imap folder straight into the archive and speeds backups up. The configuration reference lists 1 as its default; check your value with da config.
SituationDecision
Links were in ~/imap; Dovecot is changed and the links are splitKeep the check on
Links keep coming from software you cannot change, and all three conditions holdDisabling is acceptable
strict_backup_permissions=0 in an old configurationDo not disable; set it to 1 and test a backup first
A linked file in a home that its user does not own, or customers you do not knowKeep it on; investigate and fix the sources
If you disable it, put another check in place of the alarm: save the loop above with a #!/bin/bash first line as /usr/local/sbin/hardlink-audit.sh and run it weekly. The loop normally prints only when it finds something, and cron usually mails any output to root.
Bash:
chmod 700 /usr/local/sbin/hardlink-audit.sh
echo '30 3 * * 1 root /usr/local/sbin/hardlink-audit.sh' > /etc/cron.d/hardlink-audit

Step 5: Test backup, then test restore​

A backup that finishes is not yet a backup that restores. Create one for the affected account in Admin Backup/Transfer (admin level), Manage User Backups (reseller) or Site Backup (user), or from the shell:
Bash:
/usr/local/directadmin/directadmin admin-backup --destination=/home/admin/admin_backups --user=fred
ls -lh /home/admin/admin_backups/ | grep fred
Inspect it: no new Backup Abort notice should arrive, and the archive should list the parts you expect. Take the exact file name from the ls output: the middle part is not always admin, and with backup_gzip=2 the extension is .tar.zst instead of .tar.gz.
Bash:
tar -tzf /home/admin/admin_backups/user.admin.fred.tar.gz | less
zstd -dc /home/admin/admin_backups/user.admin.fred.tar.zst | tar -tf - | less   # for .tar.zst backups
Restore it on a separate test server, never over the live account. Open the site through a hosts-file entry, check the database tables and compare mailbox totals on both servers:
Bash:
doveadm mailbox status -u [email protected] -t messages '*'
Note how long the restore took; that is your real recovery time. Our guide to backups that actually restore covers the 3-2-1 rule and restore-test schedules. After moving accounts from cPanel (see the cPanel to DirectAdmin transfer guide), scan for hard links before the first scheduled backup.

Common mistakes​

  • Disabling the check first and never looking for the cause. The warning about a link to someone else's file disappears with it.
  • Deleting one name to get rid of a link. In a Maildir that removes the message from a folder.
  • Copying without -p, which changes timestamps, permissions and, as root, ownership.
  • Putting the temporary copy in /tmp. A move across filesystems is not an atomic rename.
  • Splitting links in a large mailbox without checking the quota.
  • Setting strict_backup_permissions=0 to silence permission errors. The backup then reads files with more privileges, the very case the check exists for.

FAQ​

Is this related to disk quota limits?
No. A hard link is a file with more than one name; a hard limit is a quota ceiling. Quota matters only when you split links, because each split is a full copy.

Are hard links in a mailbox dangerous?
Not when the file belongs to the user, which is the case with Dovecot's links. The check cannot tell harmless from malicious, so it fires on ordinary servers.

Will maildir_copy_with_hardlinks = no use more disk space?
Yes, for new copies. Existing links stay until you split them.

Does tar store a hard-linked file twice?
No. GNU tar stores the content once, adds link entries for the other names and recreates the links on extraction. The abort is DirectAdmin's policy, not a tar limitation.

Can a symbolic link cause this abort?
No. DirectAdmin's documentation says symbolic links are not an issue, because tar adds the link itself, not the file it points to.

We disabled the check. What now?
Keep strict_backup_permissions=1 and fs.protected_hardlinks=1, run the weekly audit and keep testing restores.

Conclusion and next steps​

The check is a blunt alarm: it cannot tell Dovecot's harmless copies from a link to /etc/shadow, so someone has to look. The source is often in ~/imap, and then one Dovecot setting plus a one-time cleanup ends the aborts with the check still on.
  1. Run the find command for the account in the notice and identify the source.
  2. Fix the source: Dovecot setting, build process, snapshot location.
  3. Split the remaining links with the copy-then-move script.
  4. Disable the check only with strict_backup_permissions=1, fs.protected_hardlinks=1 and a clean audit.
  5. Test a backup and a restore, then repeat restore tests on a schedule.
If you would rather have the audit and the restore tests handled for you, the AIOR team does this as part of Linux server management. If you see a variant of the message that is not covered here, post the full text in this thread.
 
Last edited:

Forum statistics

Threads
925
Messages
932
Members
26
Latest member
Jessicawatson

Members online

No members online now.

Featured content

AIOR
AIOR TECHNOLOGY

Get a quote for all your needs

Hosting · Domain · Servers · Design · Software · Engineering · Industry Solutions

Get a quote

24/7 Support · Quick response

Back
Top