詳解chroot

chroot 內核流程

linux 的內核源碼
SYSCALL_DEFINE1(chroot, const char __user *, filename)

-》》  ksys_chroot(const char __user *filename)

-》》》    set_fs_root(current->fs, &path);

此處當前文件系統裏面   fs->root = *path. 從而改變文件根目錄。

/*
 * Replace the fs->{rootmnt,root} with {mnt,dentry}. Put the old values.
 * It can block.
 */
void set_fs_root(struct fs_struct *fs, const struct path *path)
{
	struct path old_root;

	path_get(path);
	spin_lock(&fs->lock);
	write_seqcount_begin(&fs->seq);
	old_root = fs->root;
	fs->root = *path;
	write_seqcount_end(&fs->seq);
	spin_unlock(&fs->lock);
	if (old_root.dentry)
		path_put(&old_root);
}

 

用戶空間的使用

下面內容轉載自 https://www.ibm.com/developerworks/cn/linux/l-cn-chroot/index.html

什麼是 chroot

chroot,即 change root directory (更改 root 目錄)。在 linux 系統中,系統默認的目錄結構都是以 `/`,即是以根 (root) 開始的。而在使用 chroot 之後,系統的目錄結構將以指定的位置作爲 `/` 位置。

圖 1. Linux 系統的目錄結構

Linux 系統的目錄結構

爲何使用 chroot

在經過 chroot 之後,系統讀取到的目錄和文件將不在是舊系統根下的而是新根下(即被指定的新的位置)的目錄結構和文件,因此它帶來的好處大致有以下3個:

  1. 增加了系統的安全性,限制了用戶的權力;

    在經過 chroot 之後,在新根下將訪問不到舊系統的根目錄結構和文件,這樣就增強了系統的安全性。這個一般是在登錄 (login) 前使用 chroot,以此達到用戶不能訪問一些特定的文件。

  2. 建立一個與原系統隔離的系統目錄結構,方便用戶的開發;

    使用 chroot 後,系統讀取的是新根下的目錄和文件,這是一個與原系統根下文件不相關的目錄結構。在這個新的環境中,可以用來測試軟件的靜態編譯以及一些與系統不相關的獨立開發。

  3. 切換系統的根目錄位置,引導 Linux 系統啓動以及急救系統等。

    chroot 的作用就是切換系統的根位置,而這個作用最爲明顯的是在系統初始引導磁盤的處理過程中使用,從初始 RAM 磁盤 (initrd) 切換系統的根位置並執行真正的 init。另外,當系統出現一些問題時,我們也可以使用 chroot 來切換到一個臨時的系統。

chroot 的使用

爲了更好的理解 chroot 發揮的作用,我們將嘗試指定一個特定的位置進行根目錄切換。但是由於在經過 chroot 之後,系統讀取到的 bin/ 等與系統相關目錄將不再是舊系統根目錄下的,而是切換後新根下的目錄結構和文件,因此我們有必要準備一些目錄結構以及必要的文件。

清單 1. 準備切換的目錄結構

busybox

Busybox 被稱爲是嵌入式 Linux 中的瑞士軍刀。Busybox 包含了許多有用的命令,如 cat、find 等,但是它的體積卻非常的小。


$ pwd
/home/wstone/Build/work
$ tree .
.
|-- bin
| |-- ash -> busybox
| |-- bash
| `-- busybox
|-- etc
`-- newhome

這裏使用了靜態編譯後的 busybox 來提供必要的命令,使用靜態編譯僅是爲了避免動態庫文件的拷貝。當然我們也可以拷貝舊系統的下的命令到新的目錄結構中使用,但是那些命令通常是動態編譯的,這就意味着我們不得不拷貝相關的動態庫文件到相應的目錄結構中。同時這裏的 bash 也非真正的 Bourne Again shell,而是一個執行 ash 的 shell 腳本。在清單 2中,展示了位於舊系統中的 chroot 命令的使用。需要注意的是在使用 chroot 時,要求擁有相關的操作權限。

清單 2. 位於系統中的 chroot 的使用

1

2

3

4

5

6

7

8

9

10

11

12

13

14

$ pwd

/home/wstone/Build/work

 

# chroot .

# pwd

/

 

# ls

ash: ls: not found

 

# busybox ls

bin      etc      newhome

 

3 directories, 3 files

我們可以看到當前路徑(/home/wstone/Build/work/),在經過 chroot 後轉變成了 `/` 目錄,同時從新根下讀取了與系統相關的目錄結構。使用 ls 命令失敗是由於我們創建的測試目錄結構中並沒有包含命令 ls,但是我們成功的使用了 busybox 中的 ls。以上看到的只是 chroot 的一種使用方式,其實標準的 chroot (Coreutils - GNU core utilities 提供的 chroot)使用方式有2種:

清單 3. 標準 chroot 的2種使用方式

1

2

[1] chroot NEWROOT [COMMAND...]

[2] chroot OPTION

剛纔我們使用的是方式[2]。這將在沒有給定環境時,默認執行 `/bin/sh`,但是當給定環境後,將運行 `${SHELL} –i`,即與環境相同的可交互的 shell。我們的目錄結構中並沒有包含sh,顯然清單 2中的 chroot 運行了 `${SHELL} –i`。當然我們也可以在進行切換時指定需要的命令,即使用方式[1]。

清單 4. chroot 另一種方式的使用


# chroot . /bin/ash
#

清單 4 中,嘗試了在經過 chroot 後,執行新目錄結構下的 ash shell。不得不說的是,如果新根下的目錄結構和文件準備的夠充分,那麼一個新的簡單的 Linux 系統就可以使用了。其實更爲常見的是在初始 RAM 磁盤 (initrd)中使用 chroot,以此來執行系統的 init清單 5 中,展示的是在 Linux 2.4 內核 initrd 中使用 chroot。

清單 5. 在 Linux 2.4 內核 initrd 中使用 chroot 的示例

1

2

3

4

5

mount /dev/hda1 /new-root

cd /new-root

pivot_root . old-root

exec chroot . /sbin/init <dev/console >dev/console 2>&1

umount /old-root

由於 Linux 內核的升級,initrd 處理機制和格式發生了變化,在 Linux 2.6 內核 initrd 中不能再使用 pivot_root,因此一般也不再使用 chroot,而是選擇使用 busybox 提供的 switch_root 或者 klibc 提供的 run-init 進行根目錄的切換。(這並不是說不能在 Linux 2.6內核 initrd 中使用 chroot,選擇 switch_root 或 run-init 僅是出於習慣和方便的考慮。)但是實質上,它們僅是將 chroot 的功能進行了封裝,以此更加方便簡單的切換根目錄。

清單 6. 在 Linux 2.6 內核 initrd 中 chroot 的使用

1

2

[1] find -xdev / -exec rm '{}' ';

[2] cd /newmount; mount --move . /; chroot .

switch_root 和 run-init 完成了類似清單 6中的功能,刪除 rootfs 的全部內容以釋放空間,以及掛載新的根文件系統並進行切換。在 busybox 和 klibc中也有提供 chroot 命令,只是功能上與 Coreutils (GNU core utilities) 包含的 chroot 有稍許差異。

編寫一個 chroot

上面介紹了 chroot 及其使用,但是編寫一個簡單的 chroot 並不複雜,下面我們就嘗試編寫chroot 以此來更好的認識 chroot 的處理過程,先編寫一個粗略的 chroot 然後再完善它的功能。chroot 的編寫涉及了2個函數,chroot() 以及 chdir(),它們都包含在 unistd.h 頭文件中。

清單 7. 編寫 chroot 涉及的2個函數

1

2

3

#include <unistd.h>

int chroot(const char *path);

int chdir(const char *path);

chroot() 將切換參數 path 所指位置爲根目錄 (/),chdir() 用來將當前的工作目錄改變成以參數path 所指的目錄。以此我們可以編寫一個非常粗略的 `chroot`。

清單 8. 粗略的 `chroot`

1

2

3

4

5

6

7

8

9

10

11

12

#include <unistd.h>

 

int main(int argc, char *argv[])

{

    chroot(".");

    chdir("/");

 

    char *arrays[]={"ash",NULL};

    execvp("ash", arrays);

 

    return 0;

}

這個粗略的 `chroot` 僅能切換當前位置爲根目錄,同時默認執行 ash shell,不包含任何的錯誤處理及警告。編寫並保存代碼爲 test.c。在清單 9 中,展示了這個粗略 `chroot` 的使用情況,成功的進行了根目錄的切換。

清單 9. 粗略 `chroot` 的使用

1

2

3

4

5

6

7

8

$ gcc -Wall test.c -o test

 

# ./test

# ls

ash: ls: not found

 

# busybox ls

bin      etc      newhome  test     test.c

下面給出功能將近完整的 chroot ,加上了一些錯誤處理並新增了可執行指定命令的功能。當在沒有給出 chroot 切換後要執行的命令時,默認執行 `/bin/sh`,同時檢測環境以確認使用何種 shell。

清單 10. 功能完整的 chroot

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

#include <stdio.h>

#include <unistd.h>

#include <stdlib.h>

 

int main(int argc, char *argv[])

{

    if(argc<2){

        printf("Usage: chroot NEWROOT [COMMAND...] \n");

        return 1;

    }

 

    printf("newroot = %s\n", argv[1]);

    if(chroot(argv[1])) {

        perror("chroot");

        return 1;

    }

 

    if(chdir("/")) {

        perror("chdir");

        return 1;

    }

 

    if(argc == 2) {

        argv[0] = getenv("SHELL");

        if(!argv[0])

            argv[0] = (char *)"/bin/sh";

 

        argv[1] = (char *) "-i";

        argv[2] = NULL;

    } else {

        argv += 2;

    }

 

    execvp (argv[0], argv);

    printf("chroot: cannot run command `%s`\n", *argv);

 

    return 0;

}

保存以上代碼爲 newchroot.c 文件,編譯後運行測試其功能。最後要指出的是,本文中的 `chroot` 並沒有使用靜態編譯。如果有必要(如,在 initrd 中使用 chroot),chroot 應該使用靜態編譯,若是使用動態編譯,那麼要拷貝相關的動態庫文件到相應目錄結構中。

清單 11. `newchroot` 的測試

1

2

3

4

5

$ gcc -Wall newchroot.c -o newchroot

 

# ./newchroot . /bin/ash

newroot = .

#

結束語

在 Linux 系統初始引導的過程中,通常都有使用 chroot。但是 chroot 的好處不僅於此,它還增加了系統的安全性等。而通過本文後半部分對 chroot 的認識,我相信讀者可以更好的發揮chroot 的作用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章