لماذا لا يحفظ nano الملف؟ خطأ رفض الإذن
يظهر الخطأ "Permission denied" في nano لأربعة أسباب شائعة. افحص مالك الملف والدليل الأب، ونظام الملفات، ثم uid داخل الحاوية لإصلاح الحفظ.
لماذا لا يحفظ nano ملفك
لن يحفظ nano ملفك لسبب واحد من أربعة أسباب: لا تملك الملف، أو أن الدليل الأب لا يسمح بالعملية التي يحاول nano تنفيذها، أو أن نظام الملفات للقراءة فقط أو لا يحتوي على مساحة كافية، أو أنك تعمل داخل حاوية بمعرّف مستخدم مختلف. السببان الأولان مشكلتا صلاحيات، أما السببان الأخيران فليسا كذلك. افحص هذه الأسباب بهذا الترتيب، لأن السبب الأول يغطي معظم الحالات، ويتطلب تأكيده أمراً واحداً، وإصلاحه هو sudoedit بدلاً من sudo nano.
لا يضيع أي شيء ما دام المحرر مفتوحاً. يبقى النص في الذاكرة، ولذلك يمكنك إبقاء الملف مفتوحاً، وكتابة المحتوى المؤقت إلى مسار تملكه، ثم وضعه في مكانه بعد ذلك. ستجد هذا الحل البديل قرب نهاية هذا الدليل.
أجرِ هذه الفحوص قبل تغيير أي صلاحيات
وجّه كل أمر إلى المسار الفعلي الذي تعدّله. تجيب هذه الأوامر عن أسئلة مختلفة، لذلك شغّلها كلها قبل إجراء أي تغيير. يؤدي تغيير الصلاحيات قبل معرفة الفحص الذي يفشل عادةً إلى إنشاء مشكلة ثانية فوق المشكلة الأولى.
id
ls -l /etc/nginx/nginx.conf
ls -ld /etc/nginx
namei -l /etc/nginx/nginx.conf
findmnt -no SOURCE,FSTYPE,OPTIONS -T /etc/nginx/nginx.conf
df -h /etc/nginx
df -i /etc/nginxيعرض id معرّف المستخدم ومعرّفات المجموعات المرتبطة بحسابك حالياً. يعرض ls -l المالك والمجموعة وبتات صلاحيات الملف نفسه. ويعرض ls -ld المعلومات نفسها للدليل الذي يحتوي على الملف، وهذا سؤال مستقل له إجابة مستقلة. يتتبع namei -l كل جزء من المسار ويعرض المالك والصلاحيات لكل جزء، ولذلك يجيب عن السؤالين في مخرجات واحدة. يحدد findmnt نظام الملفات الموجود تحت ذلك المسار وخيارات mount التي استُخدمت معه. يعرض df -h المساحة المتاحة، بينما يعرض df -i inodes المتاحة، إذ تنفد هذه الموارد بشكل مستقل عن المساحة. إذا لم تكن سلاسل الصلاحيات هذه مألوفة لك بعد، فابدأ بقراءة سلسلة الصلاحيات التي يعرضها ls -l.
السبب 1: الملف مملوك للحساب root وأنت لست كذلك
أذونا القراءة والكتابة منفصلان، ومعظم الملفات الموجودة ضمن /etc يمكن للجميع قراءتها. لذلك يفتح nano الملف ويعرض محتواه ويسمح لك بالكتابة بحرية؛ فلا شيء من ذلك يكتب على القرص. يحدث الرفض عند الحفظ، عندما تقارن النواة معرّف المستخدم ومعرّفات مجموعاته ببتّات المالك والمجموعة والآخرين في الملف. ينقل nano ما أبلغته به النواة، لذلك لا يغيّر أي خيار في nano النتيجة.
يحسم id وls -l الأمر معاً. الملف مملوك للحساب root، وأنت لست root، ولا تمنح بتّات الآخرين إذن الكتابة. الضغط على Ctrl-O مرة أخرى لن يساعد.
لماذا يُعد sudoedit الطريقة الصحيحة لتحرير ملف مملوك للحساب root
SUDO_EDITOR=nano sudoedit /etc/nginx/nginx.confينشئ sudo نسخة مؤقتة من الملف، ويعيّن ملكيتها لك، ثم يشغّل nano على هذه النسخة باستخدام حسابك، وبعد خروج المحرر ينسخ النتيجة إلى مكانها بصلاحيات root. لا يعمل المحرر باستخدام حساب root. يُعد sudo -e الأمر نفسه باسم مختلف. يُحدَّد المحرر من SUDO_EDITOR، ثم VISUAL، ثم EDITOR، ولذلك فإن ضبط export EDITOR=nano في ملف تعريف الصدفة يجعل هذا المحرر الافتراضي في كل مكان. إذا كان إعداد sudoers يعطّل الخيار env_editor، فسيتم تجاهل هذه المتغيرات، ويُستخدم المحرر المحدد في إعداد editor داخل sudoers بدلاً من ذلك.
يحفظ sudo nano الملف أيضاً، وهنا تكمن المشكلة. فهو يمنح محرر تفاعلي كامل الصلاحيات root على نظام الملفات بأكمله طوال مدة الجلسة. لذلك قد يؤدي إدخال مسار خاطئ في مطالبة الحفظ إلى الكتابة فوق ملف نظام مختلف باستخدام حساب root. إن العمل باستخدام حساب مستخدم عادي لا يستدعي sudo إلا للخطوات التي تحتاج إليه هو العادة التي يجدر بك بناؤها، وsudoedit هو التطبيق العملي لهذه العادة عند تحرير ملف إعدادات.
هناك قاعدتان في sudoedit تفاجئان المستخدمين. يرفض sudoedit تحرير رابط رمزي، ويرفض تحرير ملف داخل مجلد يمكنك الكتابة إليه، إلا إذا كنت تستخدم حساب root. توجد القاعدة الثانية لأن أي شخص يستطيع الكتابة إلى المجلد يمكنه استبدال الملف أثناء فتحه في المحرر. هذان السلوكان هما الإعدادان الافتراضيان في sudoers (تعطيل sudoedit_follow، وتمكين sudoedit_checkdir). إذا لم يكن الملف موجوداً بعد، فسيُنشأ لك.
السبب 2: ما الذي يتحكم فيه الدليل الأب فعلياً
تذكر الإرشادات المكتوبة لمحررات أخرى أن الحفظ يحتاج إلى إذن الكتابة على الدليل، لأن كثيراً من المحررات تحفظ الملف بكتابة ملف جديد ثم إعادة تسميته فوق الملف القديم. لا يعمل nano بهذه الطريقة. فهو يفتح الملف الذي حددته ويكتب داخله. لذلك، إذا كان الملف موجوداً من قبل، فلا يتم الرجوع إلى بت الكتابة الخاص بالدليل مطلقاً.
يظل الدليل مسؤولاً عن أمور أخرى. ولهذا يوجد ls -ld في قائمة التحقق:
- يتطلب إنشاء ملف غير موجود بعد إذني الكتابة والتنفيذ على الدليل، لأن اسماً جديداً يجب أن يُضاف إليه. يحدد umask الأذونات التي يبدأ بها هذا الملف الجديد.
- يتطلب الوصول إلى الملف أصلاً إذن التنفيذ، ويُسمى أيضاً إذن البحث، على كل دليل في المسار. يؤدي غياب هذا الإذن في دليل واحد إلى حظر كل ما تحته، ويعرض لك
namei -lالدليل المسؤول. - يؤدي الحفظ مع تفعيل النسخ الاحتياطية أو قفل الملفات إلى كتابة ملف ثانٍ بجوار الملف الأصلي. لذلك تحتاج هذه الميزات إلى دليل قابل للكتابة. النسخ الاحتياطية هي الخيار
-Bأوset backupفي nanorc، والقفل هو-Gأوset locking. تكون الميزتان معطلتين ما لم تقم أنت أو توزيعتك بتفعيلهِما.
تحمل أذونات الدليل الأهمية نفسها في مواضع أخرى من النظام. يرفض خادم SSH استخدام مفتاح عندما يكون الدليل المنزلي أو دليل .ssh قابلاً للكتابة من مستخدمين آخرين. وهذا أحد الأسباب الشائعة لـرفض SSH لمفتاحك عند تسجيل الدخول.
لأن nano يكتب داخل الملف الموجود مسبقاً، يحتفظ الملف بـ inode الخاص به، وهو الهوية الموجودة على القرص خلف الاسم. ويستمر أي شيء يحتفظ بالملف مفتوحاً في تتبعه، كما يظل ربط ملف واحد بواسطة bind mount داخل حاوية عاملاً. أما المحررات التي تحفظ باستبدال الملف فتقطع هذا الربط، لأن الـmount يتبع inode وليس الاسم.
السبب 3: نظام الملفات للقراءة فقط أو لم تعد فيه مساحة متاحة
يعني ظهور findmnt reporting ro في الخيارات أن عملية الكتابة لم تكن ستنجح أصلاً. إما أن نظام الملفات قد رُكّب بهذه الطريقة، عبر /etc/fstab أو من خلال bind mount للقراءة فقط، أو أن kernel أعاد تركيبه للقراءة فقط بعد حدوث خطأ في القرص. الحالة الثانية هي الأخطر. يعرض sudo dmesg -T | tail -50 أخطاء الإدخال/الإخراج وأخطاء نظام الملفات التي أدت إلى إعادة التركيب، ويتطلب الإصلاح فحص نظام الملفات أثناء إلغاء تركيبه. في VPS، يعني ذلك الإقلاع إلى وحدة التحكم الخاصة بالإنقاذ التي يوفرها المزوّد.
يفشل نظام الملفات الممتلئ في عملية الكتابة نفسها، لكن لسبب مختلف. يغطي df -h الحالة العادية. ويغطي df -i الحالة التي يغفل عنها المستخدمون: تأتي inodes من مجموعة ثابتة أُنشئت عند إنشاء نظام الملفات، ويمكن لشجرة من الملفات الصغيرة جداً أن تستهلكها كلها، بينما يظل df -h يعرض غيغابايتات متاحة. عندما تنفد المساحة ولا يمسك أي شيء بها على نحو واضح، يشرح اختلاف df وdu بشأن امتلاء القرص حالة الملف المحذوف الذي لا يزال مفتوحاً، وهو ما يسبب المشكلة.
يوضح تفصيل واحد هنا سبباً لعرض محيّر. يحجز ext4 جزءاً من كتل نظام الملفات لحساب root عند إنشاء نظام الملفات، لذلك يواصل root الكتابة بعد رفض طلبات المستخدمين العاديين. يبدو sudo عندئذٍ كأنه الحل، فيمتلئ القرص بالجزء المتبقي، وتعود المشكلة في صورة أسوأ.
لأن nano يقتطع الملف قبل كتابة المحتوى الجديد، فإن الكتابة التي تنفد خلالها المساحة قد تترك الملف أقصر مما كان عليه. انسخ ملف الإعدادات المهم لك قبل تحريره على نظام ملفات أوشكت مساحته على الامتلاء. يحافظ sudo cp -a /etc/nginx/nginx.conf /root/nginx.conf.bak على المالك والمجموعة والأذونات في النسخة.
السبب 4: أنت داخل حاوية وتحرّر ملفاً في bind mount
ملكية الملفات رقمية. تخزّن النواة معرّف المستخدم، بينما يأتي الاسم الذي تراه من الجهة التي ينفّذ فيها /etc/passwd عملية البحث. لذلك قد يظهر الملف باسم على المضيف، وباسم مختلف أو برقم مجرد داخل الحاوية. قارن الأرقام بدلاً من الأسماء: شغّل id -u داخل الحاوية، وشغّل ls -ln على الملف.
يحتفظ الملف المثبّت عبر bind mount بالملكية التي لديه على المضيف. عندما يكون الملف على المضيف مملوكاً لمستخدمك، وتعمل عملية الحاوية كمستخدم مختلف، يُرفض طلب الكتابة داخل الحاوية. كما أن sudo داخل الحاوية لا يغيّر مالك الملف على المضيف. أصلح ذلك من المضيف بتعيين المالك إلى المعرّف الذي تعمل به الحاوية، أو شغّل الحاوية بالمعرّف الذي يملك الملفات مسبقاً. تكشف صور linuxserver.io ومشروعات مشابهة متغيرَي PUID وPGID اللذين يحددان المستخدم الذي تعمل به العملية.
توجد حالتان إضافيتان في الحاويات تجدر معرفتهما. يرفض mount الذي جُعل للقراءة فقط باستخدام :ro الكتابة، كما يرفضها أيضاً تشغيل الحاوية باستخدام --read-only، بغض النظر عما تشير إليه الملكية. ويعرض cat /proc/mounts داخل الحاوية هذا الخيار. مع rootless Podman، تربط مساحة أسماء المستخدمين معرّفات مستخدمي الحاوية بنطاق من معرّفات المضيف. لذلك قد يبدو الملف مملوكاً لـ root داخل الحاوية، بينما يكون مملوكاً لحسابك غير المميّز خارجها.
قد تنجح عملية التحرير ثم يختفي التغيير. إذا غيّرت ملفاً داخل حاوية في مسار ليس mount، فإنه يعيش في طبقة الكتابة الخاصة بالحاوية. تُحذف هذه الطبقة عند إعادة إنشاء الحاوية. غيّر الملف على جانب المضيف من mount، أو في عملية بناء الصورة، إذا كان يجب أن يستمر التغيير.
المخرج الآمن: احفظه في مسار تملكه
لا تحاول الحصول على الصلاحيات من داخل المحرر. اضغط Ctrl-O، وامسح المسار عند المطالبة، واكتب مساراً ضمن مجلد home الخاص بك، مثل /home/you/nginx.conf.new، ثم اضغط Enter. بعد ذلك اضغط Ctrl-X للخروج. أصبح عملك الآن محفوظاً على القرص ومملوكاً لك، وما تبقى هو نسخ ملف عادي.
sudo cp /home/you/nginx.conf.new /etc/nginx/nginx.conf
sudo nginx -tاستخدم cp هنا بدلاً من mv. يكتب cp عبر الملف الموجود مسبقاً، لذلك يحتفظ ذلك الملف بمالكه ومجموعته وصلاحياته. أما mv على نظام الملفات نفسه، فيستبدله بملفك، ما يترك ملف إعداد في /etc مملوكاً لحساب المستخدم الخاص بك. وتصبح هذه مشكلة الصلاحيات التالية التي يتعين عليك حلها.
تحقق من النتيجة باستخدام الأداة التي تدير الملف قبل إعادة تحميل أي شيء. يحلل sudo nginx -t إعدادات nginx، بينما يحلل sudo sshd -t إعدادات خادم SSH. توجد محررات مخصصة لملفين تنفذ هذا الإجراء بالكامل نيابة عنك: sudo visudo لـ /etc/sudoers، وcrontab -e لمهام cron الخاصة بك. يحرر كل منهما نسخة مؤقتة، ويتحقق من الصياغة، ولا يثبّت الملف إلا إذا اجتاز التحليل.
FAQ
هل أستخدم sudo nano أم sudoedit لتحرير ملف نظام؟
استخدم sudoedit. ينسخ الملف إلى نسخة مؤقتة يملكها حسابك، ويشغّل المحرر بحسابك أنت، ثم يكتب النتيجة بصلاحيات root عند خروج المحرر. لذلك لا يمتلك المحرر نفسه صلاحيات root مطلقاً. اضبط SUDO_EDITOR أو VISUAL أو EDITOR على nano لاختيار nano. يعمل sudo nano أيضاً، لكنه يمنح محرراً تفاعلياً صلاحية root للوصول إلى كل مسار في النظام طوال مدة الجلسة. وقد يحوّل ذلك كتابة اسم ملف خطأً عند مطالبة الحفظ إلى ملف نظام تالف.
هل أحتاج إلى صلاحية الكتابة على الدليل لحفظ ملف باستخدام nano؟
ليس للملف الموجود مسبقاً. يكتب nano داخل الملف نفسه، لذلك تتحقق النواة من بت الكتابة على الملف ومن بت التنفيذ على كل دليل في المسار. تهم بت الكتابة الخاصة بالدليل عندما لا يكون الملف موجوداً بعد، لأن إنشاء الاسم الجديد يتطلب ذلك. وتهم أيضاً عند تفعيل النسخ الاحتياطية أو قفل الملفات، لأن كلاً منهما ينشئ ملفاً ثانياً بجانب الملف الأصلي.
يبدو المالك صحيحاً والقرص غير ممتلئ. ما الذي يمكن أن يمنع الكتابة أيضاً؟
هناك أربعة أسباب. قد يكون نظام الملفات مركّباً بوضع القراءة فقط، ويعرض findmnt -no OPTIONS -T /etc/nginx/nginx.conf هذه الحالة. وقد يحمل الملف السمة immutable، ويعرض lsattr هذه السمة ويزيلها sudo chattr -i. ولا يستطيع حتى root الكتابة إلى الملف أثناء تفعيلها. وقد تنفد مجموعة inodes مع بقاء مساحة حرة، ويعرض df -i ذلك. وقد يمنع SELinux أو AppArmor الكتابة رغم سماح بتات الصلاحيات بها، ويسجل audit log الرفض للمسار الذي حاولت الكتابة إليه.
أين أضع التغييرات عندما يتعذر حفظ الملف تماماً؟
اضغط Ctrl-O وأدخل مساراً تملكه، داخل مجلد home الخاص بك أو في أي مكان آخر يمكن لحسابك الكتابة فيه. يبقى المحتوى المؤقت في الذاكرة، لذلك لا يضيع أي شيء كتبته. انسخ الملف المحفوظ إلى مكانه لاحقاً باستخدام sudo cp، إذ يحافظ ذلك على مالك الملف الأصلي وصلاحياته. ثم افحصه باستخدام أمر الاختبار الخاص بالخدمة قبل إعادة تحميلها.
لماذا تختفي تعديلاتي داخل Docker container؟
عندما لا يكون المسار mount، يُحفظ التعديل في طبقة الكتابة الخاصة بذلك container، وتُحذف هذه الطبقة عند استبدال container. حرّر الملف على جانب المضيف من bind mount أو volume، أو أدرجه في image. عندما يكون المسار bind mount ويُرفض الحفظ بدلاً من ذلك، قارن id -u داخل container بالمالك الرقمي الناتج عن ls -ln. يحتفظ الملف بملكيته على المضيف، ولذلك يجب أن يطابقها process الخاص بـcontainer.