Scroll to navigation

pivot_root(2) System Calls Manual pivot_root(2)

الاسم

pivot_root - تغيير نقطة الوصل الجذرية

المكتبة

مكتبة سي المعيارية (libc، -lc)

موجز

#include <sys/syscall.h>      /* تعريف ثوابت SYS_* */
#include <unistd.h>
int syscall(SYS_pivot_root, const char *new_root, const char *put_old);

ملاحظة: لا توفر glibc غلافًا لـ pivot_root()، مما يستلزم استخدام syscall(2).

الوصف

يغير pivot_root() نقطة الوصل الجذرية في مساحة أسماء الوصل الخاصة بالعملية المستدعية. بشكل أدق، ينقل نقطة الوصل الجذرية إلى الدليل put_old ويجعل new_root نقطة الوصل الجذرية الجديدة. يجب أن تمتلك العملية المستدعية إمكانية CAP_SYS_ADMIN في مساحة أسماء المستخدم التي تمتلك مساحة أسماء الوصل الخاصة بالمستدعي.

يغير pivot_root() الدليل الجذري ودليل العمل الحالي لكل عملية أو خيط في نفس مساحة أسماء الوصل إلى new_root إذا كانا يشيران إلى الدليل الجذري القديم. (انظر أيضًا الملاحظات.) من ناحية أخرى، لا يغير pivot_root() دليل العمل الحالي للمستدعي (إلا إذا كان على الدليل الجذري القديم)، وبالتالي يجب أن يتبعه استدعاء chdir("/").

تنطبق القيود التالية:

يجب أن يكون new_root و put_old دليلين.
يجب ألا يكون new_root و put_old على نفس نقطة الوصل مثل الجذر الحالي.
يجب أن يكون put_old عند أو أسفل new_root؛ أي أن إضافة عدد غير سالب من اللواحق "/.." إلى اسم المسار المشار إليه بواسطة put_old يجب أن ينتج نفس الدليل مثل new_root.
يجب أن يكون new_root مسارًا إلى نقطة وصل، لكن لا يمكن أن يكون "/". يمكن تحويل مسار ليس بالفعل نقطة وصل إلى واحدة عن طريق وصل ربط المسار على نفسه.
يجب ألا يكون نوع الانتشار لنقطة الوصل الأم لـ new_root ونقطة الوصل الأم للدليل الجذري الحالي MS_SHARED؛ وبالمثل، إذا كان put_old نقطة وصل موجودة، يجب ألا يكون نوع انتشارها MS_SHARED. تضمن هذه القيود أن pivot_root() لا ينشر أي تغييرات أبدًا إلى مساحة أسماء وصل أخرى.
يجب أن يكون الدليل الجذري الحالي نقطة وصل.

قيمة الإرجاع

عند النجاح، يُعاد الصفر. وعند حدوث خطأ، يُعاد الرقم -1، ويُضبط errno للإشارة إلى الخطأ.

الأخطاء

قد يفشل pivot_root() مع أي من نفس الأخطاء مثل stat(2). بالإضافة إلى ذلك، قد يفشل مع الأخطاء التالية:

new_root أو put_old على نقطة الوصل الجذرية الحالية. (يغطي هذا الخطأ الحالة المرضية حيث new_root هو "/".)
new_root ليس نقطة وصل.
put_old ليس عند أو أسفل new_root.
الدليل الجذري الحالي ليس نقطة وصل (بسبب chroot(2) سابق).
الجذر الحالي على نقطة وصل rootfs (ramfs الأولية)؛ انظر الملاحظات.
إما نقطة الوصل عند new_root، أو نقطة الوصل الأم لتلك النقطة، لها نوع انتشار MS_SHARED.
put_old هو نقطة وصل وله نوع انتشار MS_SHARED.
new_root أو put_old ليس دليلاً.
لا تمتلك العملية المستدعية إمكانية CAP_SYS_ADMIN.

المعايير

لينكس.

التاريخ

لينكس 2.3.41.

ملاحظات

توفر pivot_root(8) واجهة سطر أوامر لاستدعاء النظام هذا.

يسمح pivot_root() للمستدعي بالتبديل إلى نظام ملفات جذري جديد مع وضع نقطة الوصل الجذرية القديمة في موقع تحت new_root حيث يمكن فصلها لاحقًا. (حقيقة أنه ينقل جميع العمليات التي لديها دليل جذري أو دليل عمل حالي على الدليل الجذري القديم إلى الجذر الجديد يحرر الدليل الجذري القديم من المستخدمين، مما يسمح بفصل نقطة الوصل الجذرية القديمة بسهولة أكبر.)

أحد استخدامات pivot_root() هو أثناء بدء تشغيل النظام، عندما يقوم النظام بوصل نظام ملفات جذري مؤقت (مثل initrd(4))، ثم يصل نظام الملفات الجذري الحقيقي، ويحول الأخير في النهاية إلى الدليل الجذري لجميع العمليات والخيوط ذات الصلة. استخدام حديث هو إعداد نظام ملفات جذري أثناء إنشاء حاوية.

حقيقة أن pivot_root() يعدل الدلائل الجذرية ودلائل العمل الحالية للعمليات بالطريقة المذكورة في الوصف ضرورية لمنع خيوط النواة من إبقاء نقطة الوصل الجذرية القديمة مشغولة بدلائلها الجذرية ودلائل العمل الحالية، حتى لو لم تصل إلى نظام الملفات بأي شكل.

لا يمكن تطبيق pivot_root() على rootfs (ramfs الأولية). الطريقة الموصى بها لتغيير نظام الملفات الجذري في هذه الحالة هي حذف كل شيء في rootfs، ووصل rootfs فوقه بالجذر الجديد، وإرفاق stdin/stdout/stderr بـ /dev/console الجديد، وتنفيذ init(1) الجديد. توجد برامج مساعدة لهذه العملية؛ انظر switch_root(8).

pivot_root(".", ".")

يمكن أن يكون new_root و put_old نفس الدليل. على وجه الخصوص، يسمح التسلسل التالي بعملية pivot-root دون الحاجة إلى إنشاء وإزالة دليل مؤقت:


chdir(new_root);
pivot_root(".", ".");
umount2(".", MNT_DETACH);

ينجح هذا التسلسل لأن استدعاء pivot_root() يكدس نقطة وصل الجذر القديم فوق نقطة وصل الجذر الجديد في /. في تلك النقطة، يشير دليل الجذر ودليل العمل الحالي لعملية الاستدعاء إلى نقطة وصل الجذر الجديد (new_root). أثناء استدعاء umount() اللاحق، يبدأ تحليل "." من new_root ثم يتحرك لأعلى قائمة الوصلات المكدسة في /، مما يؤدي إلى فك وصل نقطة وصل الجذر القديم.

ملاحظات تاريخية

لسنوات عديدة، حملت صفحة الدليل هذه النص التالي:

قد يغير pivot_root() أو لا يغير الجذر الحالي ودليل العمل الحالي لأي عمليات أو خيوط تستخدم دليل الجذر القديم. يجب على مستدعي pivot_root() التأكد من أن العمليات التي لها جذر أو دليل عمل حالي في الجذر القديم تعمل بشكل صحيح في كلتا الحالتين. طريقة سهلة لضمان ذلك هي تغيير جذرها ودليل عملها الحالي إلى new_root قبل استدعاء pivot_root().

هذا النص، الذي كتب قبل حتى الانتهاء من تنفيذ استدعاء النظام في النواة، ربما كان يهدف إلى تحذير المستخدمين في ذلك الوقت بأن التنفيذ قد يتغير قبل الإصدار النهائي. ومع ذلك، ظل السلوك المذكور في الوصف ثابتًا منذ تنفيذ استدعاء النظام هذا لأول مرة ولن يتغير الآن.

أمثلة

يوضح البرنامج أدناه استخدام pivot_root() داخل مساحة اسم وصل تم إنشاؤها باستخدام clone(2). بعد التمحور إلى دليل الجذر المسمى في وسيطة سطر الأوامر الأولى للبرنامج، ينفذ الطفل الذي أنشأه clone(2) البرنامج المسمى في وسيطات سطر الأوامر المتبقية.

نوضح البرنامج عن طريق إنشاء دليل سيعمل كنظام ملفات جذر جديد ووضع نسخة من الملف التنفيذي busybox(1) (المرتبط بشكل ثابت) في ذلك الدليل.


$ mkdir /tmp/rootfs;
$ ls -id /tmp/rootfs;    # Show inode number of new root directory
319459 /tmp/rootfs
$ cp $(which busybox) /tmp/rootfs;
$ PS1='bbsh$ ' sudo ./pivot_root_demo /tmp/rootfs /busybox sh;
bbsh$ PATH=/;
bbsh$ busybox ln busybox ln;
bbsh$ ln busybox echo;
bbsh$ ln busybox ls;
bbsh$ ls;
busybox  echo     ln       ls
bbsh$ ls -id /;          # Compare with inode number above
319459 /
bbsh$ echo 'hello world';
hello world

مصدر البرنامج

/* pivot_root_demo.c */
#define _GNU_SOURCE
#include <err.h>
#include <limits.h>
#include <sched.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/wait.h>
#include <unistd.h>
static int
pivot_root(const char *new_root, const char *put_old)
{

return syscall(SYS_pivot_root, new_root, put_old); } #define STACK_SIZE (1024 * 1024) static int /* Startup function for cloned child */ child(void *arg) {
char path[PATH_MAX];
char **args = arg;
char *new_root = args[0];
const char *put_old = "/oldrootfs";
/* Ensure that 'new_root' and its parent mount don't have
shared propagation (which would cause pivot_root() to
return an error), and prevent propagation of mount
events to the initial mount namespace. */
if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL) == -1)
err(EXIT_FAILURE, "mount-MS_PRIVATE");
/* Ensure that 'new_root' is a mount point. */
if (mount(new_root, new_root, NULL, MS_BIND, NULL) == -1)
err(EXIT_FAILURE, "mount-MS_BIND");
/* Create directory to which old root will be pivoted. */
snprintf(path, sizeof(path), "%s/%s", new_root, put_old);
if (mkdir(path, 0777) == -1)
err(EXIT_FAILURE, "mkdir");
/* And pivot the root filesystem. */
if (pivot_root(new_root, path) == -1)
err(EXIT_FAILURE, "pivot_root");
/* Switch the current working directory to "/". */
if (chdir("/") == -1)
err(EXIT_FAILURE, "chdir");
/* Unmount old root and remove mount point. */
if (umount2(put_old, MNT_DETACH) == -1)
perror("umount2");
if (rmdir(put_old) == -1)
perror("rmdir");
/* Execute the command specified in argv[1]... */
execv(args[1], &args[1]);
err(EXIT_FAILURE, "execv"); } int main(int argc, char *argv[]) {
char *stack;
/* Create a child process in a new mount namespace. */
stack = mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
if (stack == MAP_FAILED)
err(EXIT_FAILURE, "mmap");
if (clone(child, stack + STACK_SIZE,
CLONE_NEWNS | SIGCHLD, &argv[1]) == -1)
err(EXIT_FAILURE, "clone");
/* Parent falls through to here; wait for child. */
if (wait(NULL) == -1)
err(EXIT_FAILURE, "wait");
exit(EXIT_SUCCESS); }

انظر أيضًا

chdir(2), chroot(2), mount(2), stat(2), initrd(4), mount_namespaces(7), pivot_root(8), switch_root(8)

ترجمة

تُرجمت هذه الصفحة من الدليل بواسطة زايد السعيدي <zayed.alsaidi@gmail.com>

هذه الترجمة هي وثيقة مجانية؛ راجع رخصة جنو العامة الإصدار 3 أو ما بعده للاطلاع على شروط حقوق النشر. لا توجد أي ضمانات.

إذا وجدت أي أخطاء في ترجمة صفحة الدليل هذه، يرجى إرسال بريد إلكتروني إلى قائمة بريد المترجمين: kde-l10n-ar@kde.org.

8 فبراير 2026 صفحات دليل لينكس 6.18