分析內核初始化時根內存盤的加載過程


概述
====
1)當內核配置了內存盤時, 內核在初始化時可以將軟盤加載到內存盤中作爲根盤.
當同時配置了初始化內
存盤(Initail RAM Disk)時, 內核在初始化時可以在安裝主盤之前,
通過引導程序所加載的initrd文件建
立一個內存初始化盤, 首先將它安裝成根文件系統, 然後執行其根目錄下的linuxrc 文件,
可用於在安裝
主盤之前加載一些內核模塊. 等到linuxrc 程序退出後, 再將主盤安裝成根文件系統,
並將內存初始化盤
轉移安裝到其/initrd目錄下.

2)當主盤就是initrd所生成的內存初始化盤時, 不再進行重新安裝,
在DOS下用loadlin加載的搶救盤就是
這種工作方式.

3)引導程序所加載的initrd爲文件系統的映象文件, 可以是gzip壓縮的, 也可以是不壓縮的.
能夠識別的
文件系統有minix,ext2,romfs三種.

4)當內核的根盤爲軟盤時,
內核初始化時會測試軟盤的指定部位是否存在文件系統或壓縮文件映象, 然後
將之加載或解壓到內存盤中作爲根盤. 這是單張搶救軟盤的工作方式.

有關代碼
========
; init/main.c

#ifdef CONFIG_BLK_DEV_INITRD
kdev_t real_root_dev; 啓動參數所設定的根盤設備
#endif

asmlinkage void __init start_kernel(void)
{
char * command_line;
unsigned long mempages;
extern char saved_command_line[];

lock_kernel();
printk(linux_banner);
setup_arch(&command_line);
arch/i386/kernel/setup.c中,初始化initrd_start和initrd_end兩個變量

...

#ifdef CONFIG_BLK_DEV_INITRD
if (initrd_start && !initrd_below_start_ok &&
initrd_start < min_low_pfn << PAGE_SHIFT) {
; min_low_pfn爲內核末端_end所開始的物理頁號,initrd_start,initrd_end在rd.c中定義
printk(KERN_CRIT "initrd overwritten (0x%08lx < 0x%08lx) - "
"disabling it./n",initrd_start,min_low_pfn << PAGE_SHIFT);
initrd_start = 0;
}
#endif
...

kernel_thread(init, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGNAL); 創建init進程
unlock_kernel();
current->need_resched = 1;
cpu_idle();
}
static int init(void * unused)
{
lock_kernel();
do_basic_setup();

/*
* Ok, we have completed the initial bootup, and
* we're essentially up and running. Get rid of the
* initmem segments and start the user-mode stuff..
*/
free_initmem();
unlock_kernel();

if (open("/dev/console", O_RDWR, 0) < 0)
printk("Warning: unable to open an initial console./n");

(void) dup(0);
(void) dup(0);

/*
* We try each of these until one succeeds.
*
* The Bourne shell can be used instead of init if we are
* trying to recover a really broken machine.
*/

if (execute_command)
execve(execute_command,argv_init,envp_init);
execve("/sbin/init",argv_init,envp_init);
execve("/etc/init",argv_init,envp_init);
execve("/bin/init",argv_init,envp_init);
execve("/bin/sh",argv_init,envp_init);
panic("No init found. Try passing init= option to kernel.");
}

static void __init do_basic_setup(void)
{
#ifdef CONFIG_BLK_DEV_INITRD
int real_root_mountflags;
#endif

...

#ifdef CONFIG_BLK_DEV_INITRD
real_root_dev = ROOT_DEV; ROOT_DEV爲所請求根文件系統的塊設備
real_root_mountflags = root_mountflags;
if (initrd_start && mount_initrd) root_mountflags &= ~MS_RDONLY;
else mount_initrd =0;
#endif

start_context_thread();
do_initcalls(); 會調用partition_setup()中加載內存盤

/* .. filesystems .. */
filesystem_setup();

/* Mount the root filesystem.. */
mount_root();
mount_devfs_fs ();

#ifdef CONFIG_BLK_DEV_INITRD
root_mountflags = real_root_mountflags;
if (mount_initrd && ROOT_DEV != real_root_dev
&& MAJOR(ROOT_DEV) == RAMDISK_MAJOR && MINOR(ROOT_DEV) == 0) {
; 如果當前根盤爲initrd所建立的內存盤
int error;
int i, pid;

pid = kernel_thread(do_linuxrc, "/linuxrc", SIGCHLD); 創建新的任務去執行程序/linuxrc
if (pid>0)
while (pid != wait(&i)); 等待linuxrc進程退出
if (MAJOR(real_root_dev) != RAMDISK_MAJOR
|| MINOR(real_root_dev) != 0) {
; 如果原來的根盤不是0號內存盤,則使用原來的根文件系統,
; 並且將內存盤轉移到其/initrd目錄下
error = change_root(real_root_dev,"/initrd");
if (error)
printk(KERN_ERR "Change root to /initrd: "
"error %d/n",error);
}
}
#endif
}

#ifdef CONFIG_BLK_DEV_INITRD
static int do_linuxrc(void * shell)
{
static char *argv[] = { "linuxrc", NULL, };

close(0);close(1);close(2);
setsid(); 設置新的session號
(void) open("/dev/console",O_RDWR,0);
(void) dup(0);
(void) dup(0);
return execve(shell, argv, envp_init);
}

#endif

; arch/i386/kernel/setup.c

#define RAMDISK_IMAGE_START_MASK 0x07FF
#define RAMDISK_PROMPT_FLAG 0x8000
#define RAMDISK_LOAD_FLAG 0x4000

#define PARAM ((unsigned char *)empty_zero_page)
#define RAMDISK_FLAGS (*(unsigned short *) (PARAM+0x1F8)) 可用rdev設置的參數
#define LOADER_TYPE (*(unsigned char *) (PARAM+0x210))
#define INITRD_START (*(unsigned long *) (PARAM+0x218)) 初始化盤映象起始物理地址
#define INITRD_SIZE (*(unsigned long *) (PARAM+0x21c)) 初始化盤字節數

void __init setup_arch(char **cmdline_p)
{
...
#ifdef CONFIG_BLK_DEV_RAM
rd_image_start = RAMDISK_FLAGS & RAMDISK_IMAGE_START_MASK; 以塊爲單位
rd_prompt = ((RAMDISK_FLAGS & RAMDISK_PROMPT_FLAG) != 0);
rd_doload = ((RAMDISK_FLAGS & RAMDISK_LOAD_FLAG) != 0);
#endif
...
#ifdef CONFIG_BLK_DEV_INITRD
if (LOADER_TYPE && INITRD_START) {
if (INITRD_START + INITRD_SIZE <= (max_low_pfn << PAGE_SHIFT)) {
; max_low_pfn表示內核空間1G範圍以下最大允許的物理頁號
reserve_bootmem(INITRD_START, INITRD_SIZE);
initrd_start =
INITRD_START ? INITRD_START + PAGE_OFFSET : 0; 轉變爲內核邏輯地址
initrd_end = initrd_start+INITRD_SIZE;
}
else {
printk("initrd extends beyond end of memory "
"(0x%08lx > 0x%08lx)/ndisabling initrd/n",
INITRD_START + INITRD_SIZE,
max_low_pfn << PAGE_SHIFT);
initrd_start = 0;
}
}
#endif
...
}

; fs/partitions/check.c:

int __init partition_setup(void)
{
device_init(); 包含ramdisk設備的初始化

#ifdef CONFIG_BLK_DEV_RAM
#ifdef CONFIG_BLK_DEV_INITRD
if (initrd_start && mount_initrd) initrd_load();
;如果啓動時加載了initrd文件,則用它去初始化根內存盤
else
#endif
rd_load(); 如果內核配置了內存盤並且根盤指定爲軟盤則試圖將軟盤加載爲根內存盤
#endif
return 0;
}
__initcall(partition_setup);

; drivers/block/rd.c:

int rd_doload; /* 1 = load RAM disk, 0 = don't load */
int rd_prompt = 1; /* 1 = prompt for RAM disk, 0 = don't prompt */
int rd_image_start; /* starting block # of image */
#ifdef CONFIG_BLK_DEV_INITRD
unsigned long initrd_start, initrd_end;
int mount_initrd = 1; /* zero if initrd should not be mounted */
int initrd_below_start_ok;

void __init rd_load(void)
{
rd_load_disk(0); 加載到0號內存盤
}
void __init rd_load_secondary(void)
{
rd_load_disk(1); 加載到1號內存盤
}
static void __init rd_load_disk(int n)
{
#ifdef CONFIG_BLK_DEV_INITRD
extern kdev_t real_root_dev;
#endif

if (rd_doload == 0)
return;

if (MAJOR(ROOT_DEV) != FLOPPY_MAJOR 如果根盤是不軟盤
#ifdef CONFIG_BLK_DEV_INITRD
&& MAJOR(real_root_dev) != FLOPPY_MAJOR
#endif
)
return;

if (rd_prompt) {
#ifdef CONFIG_BLK_DEV_FD
floppy_eject();
#endif
#ifdef CONFIG_MAC_FLOPPY
if(MAJOR(ROOT_DEV) == FLOPPY_MAJOR)
swim3_fd_eject(MINOR(ROOT_DEV));
else if(MAJOR(real_root_dev) == FLOPPY_MAJOR)
swim3_fd_eject(MINOR(real_root_dev));
#endif
printk(KERN_NOTICE
"VFS: Insert root floppy disk to be loaded into RAM disk and press ENTER/n");
wait_for_keypress();
}

rd_load_image(ROOT_DEV,rd_image_start, n); 將根軟盤加載到n號內存盤

}
void __init initrd_load(void)
{
; 使用initrd設備盤作爲源盤去建立內存根盤
rd_load_image(MKDEV(MAJOR_NR, INITRD_MINOR),rd_image_start,0);
}
static void __init rd_load_image(kdev_t device, int offset, int unit)
{
struct inode *inode, *out_inode;
struct file infile, outfile;
struct dentry in_dentry, out_dentry;
mm_segment_t fs;
kdev_t ram_device;
int nblocks, i;
char *buf;
unsigned short rotate = 0;
unsigned short devblocks = 0;
char rotator[4] = { '|' , '/' , '-' , '//' };

ram_device = MKDEV(MAJOR_NR, unit); 建立輸出內存盤設備號

if ((inode = get_empty_inode()) == NULL)
return;
memset(&infile, 0, sizeof(infile));
memset(&in_dentry, 0, sizeof(in_dentry));
infile.f_mode = 1; /* read only */
infile.f_dentry = &in_dentry;
in_dentry.d_inode = inode;
infile.f_op = &def_blk_fops;
init_special_inode(inode, S_IFBLK | S_IRUSR, kdev_t_to_nr(device));

if ((out_inode = get_empty_inode()) == NULL)
goto free_inode;
memset(&outfile, 0, sizeof(outfile));
memset(&out_dentry, 0, sizeof(out_dentry));
outfile.f_mode = 3; /* read/write */
outfile.f_dentry = &out_dentry;
out_dentry.d_inode = out_inode;
outfile.f_op = &def_blk_fops;
init_special_inode(out_inode, S_IFBLK | S_IRUSR | S_IWUSR, kdev_t_to_nr(ram_device));

if (blkdev_open(inode, &infile) != 0) 打開輸入盤文件
goto free_inode;
if (blkdev_open(out_inode, &outfile) != 0) 打開輸出內存盤文件
goto free_inodes;

fs = get_fs();
set_fs(KERNEL_DS);

nblocks = identify_ramdisk_image(device, &infile, offset); 鑑定輸入盤的文件類型
if (nblocks < 0) 出錯
goto done;

if (nblocks == 0) { 表示輸入盤是gzip文件
#ifdef BUILD_CRAMDISK
if (crd_load(&infile, &outfile) == 0) 將輸入盤文件解壓到輸出盤文件中去
goto successful_load;
#else
printk(KERN_NOTICE
"RAMDISK: Kernel does not support compressed "
"RAM disk images/n");
#endif
goto done;
}

/*
* NOTE NOTE: nblocks suppose that the blocksize is BLOCK_SIZE, so
* rd_load_image will work only with filesystem BLOCK_SIZE wide!
* So make sure to use 1k blocksize while generating ext2fs
* ramdisk-images.
*/
if (nblocks > (rd_length[unit] >> BLOCK_SIZE_BITS)) {
; 如果輸入盤的尺寸超過了輸出內存盤的允許尺寸
printk("RAMDISK: image too big! (%d/%ld blocks)/n",
nblocks, rd_length[unit] >> BLOCK_SIZE_BITS);
goto done;
}

/*
* OK, time to copy in the data
*/
buf = kmalloc(BLOCK_SIZE, GFP_KERNEL);
if (buf == 0) {
printk(KERN_ERR "RAMDISK: could not allocate buffer/n");
goto done;
}

if (blk_size[MAJOR(device)])
devblocks = blk_size[MAJOR(device)][MINOR(device)]; 取輸入盤的容量

#ifdef CONFIG_BLK_DEV_INITRD
if (MAJOR(device) == MAJOR_NR && MINOR(device) == INITRD_MINOR)
devblocks = nblocks; 如果輸入是初始化內存盤,則盤的容量爲它的實際尺寸
#endif

if (devblocks == 0) {
printk(KERN_ERR "RAMDISK: could not determine device size/n");
goto done;
}

printk(KERN_NOTICE "RAMDISK: Loading %d blocks [%d disk%s] into ram disk... ",
nblocks, ((nblocks-1)/devblocks)+1, nblocks>devblocks ? "s" : "");
for (i=0; i < nblocks; i++) {
if (i && (i % devblocks == 0)) {
printk("done disk #%d./n", i/devblocks);
rotate = 0;
invalidate_buffers(device); 使輸入盤設備緩衝區無效
if (infile.f_op->release)
infile.f_op->release(inode, &infile);
printk("Please insert disk #%d and press ENTER/n", i/devblocks+1);
wait_for_keypress();
if (blkdev_open(inode, &infile) != 0) {
printk("Error opening disk./n");
goto done;
}
infile.f_pos = 0;
printk("Loading disk #%d... ", i/devblocks+1);
}
infile.f_op->read(&infile, buf, BLOCK_SIZE, &infile.f_pos);
outfile.f_op->write(&outfile, buf, BLOCK_SIZE, &outfile.f_pos);
#if !defined(CONFIG_ARCH_S390)
if (!(i % 16)) {
printk("%c/b", rotator[rotate & 0x3]);
rotate++;
}
#endif
}
printk("done./n");
kfree(buf);

successful_load:
invalidate_buffers(device);
ROOT_DEV = MKDEV(MAJOR_NR, unit); 將根盤設備設置爲當前加載的內存盤
if (ROOT_DEVICE_NAME != NULL) strcpy (ROOT_DEVICE_NAME, "rd/0");

done:
if (infile.f_op->release)
infile.f_op->release(inode, &infile);
set_fs(fs);
return;
free_inodes: /* free inodes on error */
iput(out_inode);
blkdev_put(inode->i_bdev, BDEV_FILE);
free_inode:
iput(inode);
}
int __init
identify_ramdisk_image(kdev_t device, struct file *fp, int start_block)
{
const int size = 512;
struct minix_super_block *minixsb;
struct ext2_super_block *ext2sb;
struct romfs_super_block *romfsb;
int nblocks = -1;
unsigned char *buf;

buf = kmalloc(size, GFP_KERNEL);
if (buf == 0)
return -1;

minixsb = (struct minix_super_block *) buf;
ext2sb = (struct ext2_super_block *) buf;
romfsb = (struct romfs_super_block *) buf;
memset(buf, 0xe5, size);

/*
* Read block 0 to test for gzipped kernel
*/
if (fp->f_op->llseek)
fp->f_op->llseek(fp, start_block * BLOCK_SIZE, 0);
fp->f_pos = start_block * BLOCK_SIZE;

fp->f_op->read(fp, buf, size, &fp->f_pos);
; 讀取offset開始的512字節
/*
* If it matches the gzip magic numbers, return -1
*/
if (buf[0] == 037 && ((buf[1] == 0213) || (buf[1] == 0236))) {
printk(KERN_NOTICE
"RAMDISK: Compressed image found at block %d/n",
start_block);
nblocks = 0;
goto done;
}

/* romfs is at block zero too */
if (romfsb->word0 == ROMSB_WORD0 &&
romfsb->word1 == ROMSB_WORD1) {
printk(KERN_NOTICE
"RAMDISK: romfs filesystem found at block %d/n",
start_block);
nblocks = (ntohl(romfsb->size)+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS;
goto done;
}

/*
* Read block 1 to test for minix and ext2 superblock
*/
if (fp->f_op->llseek)
fp->f_op->llseek(fp, (start_block+1) * BLOCK_SIZE, 0);
fp->f_pos = (start_block+1) * BLOCK_SIZE;

fp->f_op->read(fp, buf, size, &fp->f_pos);

/* Try minix */
if (minixsb->s_magic == MINIX_SUPER_MAGIC ||
minixsb->s_magic == MINIX_SUPER_MAGIC2) {
printk(KERN_NOTICE
"RAMDISK: Minix filesystem found at block %d/n",
start_block);
nblocks = minixsb->s_nzones << minixsb->s_log_zone_size;
goto done;
}

/* Try ext2 */
if (ext2sb->s_magic == cpu_to_le16(EXT2_SUPER_MAGIC)) {
printk(KERN_NOTICE
"RAMDISK: ext2 filesystem found at block %d/n",
start_block);
nblocks = le32_to_cpu(ext2sb->s_blocks_count);
goto done;
}

printk(KERN_NOTICE
"RAMDISK: Couldn't find valid RAM disk image starting at %d./n",
start_block);

done:
if (fp->f_op->llseek)
fp->f_op->llseek(fp, start_block * BLOCK_SIZE, 0);
fp->f_pos = start_block * BLOCK_SIZE;

kfree(buf);
return nblocks;
}

; fs/super.c
void __init mount_root(void)
{
struct file_system_type * fs_type;
struct super_block * sb;
struct vfsmount *vfsmnt;
struct block_device *bdev = NULL;
mode_t mode;
int retval;
void *handle;
char path[64];
int path_start = -1;


#ifdef CONFIG_BLK_DEV_FD
if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) { 當根盤還是軟盤,表示沒有加載過內存盤
#ifdef CONFIG_BLK_DEV_RAM
extern int rd_doload;
extern void rd_load_secondary(void);
#endif
floppy_eject();
#ifndef CONFIG_BLK_DEV_RAM
printk(KERN_NOTICE "(Warning, this kernel has no ramdisk support)/n");
#else
/* rd_doload is 2 for a dual initrd/ramload setup */
; 只有當加載了initrd但沒有釋放到內存盤中(mount_inird=0)纔有可能到這一步
if(rd_doload==2)
rd_load_secondary(); 加載另一張軟盤到1號內存盤作爲根盤
else
#endif
{
printk(KERN_NOTICE "VFS: Insert root floppy and press ENTER/n");
wait_for_keypress();
}
}
#endif

devfs_make_root (root_device_name);
handle = devfs_find_handle (NULL, ROOT_DEVICE_NAME,
MAJOR (ROOT_DEV), MINOR (ROOT_DEV),
DEVFS_SPECIAL_BLK, 1);
if (handle) /* Sigh: bd*() functions only paper over the cracks */
{
unsigned major, minor;

devfs_get_maj_min (handle, &major, &minor);
ROOT_DEV = MKDEV (major, minor);
}

/*
* Probably pure paranoia, but I'm less than happy about delving into
* devfs crap and checking it right now. Later.
*/
if (!ROOT_DEV)
panic("I have no root and I want to scream");

bdev = bdget(kdev_t_to_nr(ROOT_DEV));
if (!bdev)
panic(__FUNCTION__ ": unable to allocate root device");
bdev->bd_op = devfs_get_ops (handle);
path_start = devfs_generate_path (handle, path + 5, sizeof (path) - 5);
mode = FMODE_READ;
if (!(root_mountflags & MS_RDONLY))
mode |= FMODE_WRITE;
retval = blkdev_get(bdev, mode, 0, BDEV_FS);
if (retval == -EROFS) {
root_mountflags |= MS_RDONLY;
retval = blkdev_get(bdev, FMODE_READ, 0, BDEV_FS);
}
if (retval) {
/*
* Allow the user to distinguish between failed open
* and bad superblock on root device.
*/
printk ("VFS: Cannot open root device /"%s/" or %s/n",
root_device_name, kdevname (ROOT_DEV));
printk ("Please append a correct /"root=/" boot option/n");
panic("VFS: Unable to mount root fs on %s",
kdevname(ROOT_DEV));
}

check_disk_change(ROOT_DEV);
sb = get_super(ROOT_DEV); 取根盤的超級塊
if (sb) {
fs_type = sb->s_type;
goto mount_it;
}

read_lock(&file_systems_lock);
for (fs_type = file_systems ; fs_type ; fs_type = fs_type->next) {
if (!(fs_type->fs_flags & FS_REQUIRES_DEV))
continue; 根文件系統必須依賴於塊設備
if (!try_inc_mod_count(fs_type->owner))
continue; 當文件系統模塊正在刪除過程中
read_unlock(&file_systems_lock);
sb = read_super(ROOT_DEV,bdev,fs_type,root_mountflags,NULL,1);
建立根盤的超級塊結構
if (sb)
goto mount_it;
read_lock(&file_systems_lock);
put_filesystem(fs_type); 釋放對文件系統模塊的引用
}
read_unlock(&file_systems_lock);
panic("VFS: Unable to mount root fs on %s", kdevname(ROOT_DEV));

mount_it:
printk ("VFS: Mounted root (%s filesystem)%s./n",
fs_type->name,
(sb->s_flags & MS_RDONLY) ? " readonly" : "");
if (path_start >= 0) {
devfs_mk_symlink (NULL, "root", DEVFS_FL_DEFAULT,
path + 5 + path_start, NULL, NULL);
memcpy (path + path_start, "/dev/", 5);
vfsmnt = add_vfsmnt(NULL, sb->s_root, path + path_start);
}
else
vfsmnt = add_vfsmnt(NULL, sb->s_root, "/dev/root"); 建立根盤的安裝結構
/* FIXME: if something will try to umount us right now... */
if (vfsmnt) {
set_fs_root(current->fs, vfsmnt, sb->s_root); 設置當前進程的根盤和根目錄
set_fs_pwd(current->fs, vfsmnt, sb->s_root); 設置當前進程的當前盤和當前目錄
if (bdev)
bdput(bdev); /* sb holds a reference */
return;
}
panic("VFS: add_vfsmnt failed for root fs");
}

#ifdef CONFIG_BLK_DEV_INITRD
int __init change_root(kdev_t new_root_dev,const char *put_old)
{ 以new_root_dev作爲根盤重新安裝根文件系統,原來的根轉移到put_old目錄下
struct vfsmount *old_rootmnt;
struct nameidata devfs_nd, nd;
int error = 0;

read_lock(¤t->fs->lock);
old_rootmnt = mntget(current->fs->rootmnt); 取當前進程的根盤安裝結構
read_unlock(¤t->fs->lock);
/* First unmount devfs if mounted */
if (path_init("/dev", LOOKUP_FOLLOW|LOOKUP_POSITIVE, &devfs_nd))
error = path_walk("/dev", &devfs_nd);
if (!error) {
if (devfs_nd.mnt->mnt_sb->s_magic == DEVFS_SUPER_MAGIC &&
devfs_nd.dentry == devfs_nd.mnt->mnt_root) {
dput(devfs_nd.dentry);
down(&mount_sem);
/* puts devfs_nd.mnt */
do_umount(devfs_nd.mnt, 0, 0);
up(&mount_sem);
} else
path_release(&devfs_nd);
}
ROOT_DEV = new_root_dev;
mount_root(); 改變根盤設備重新安裝根文件系統
#if 1
shrink_dcache(); 清除目錄項緩衝中所有自由的目錄項
printk("change_root: old root has d_count=%d/n",
atomic_read(&old_rootmnt->mnt_root->d_count));
#endif
mount_devfs_fs ();
/*
* Get the new mount directory
*/
error = 0;
if (path_init(put_old, LOOKUP_FOLLOW|LOOKUP_POSITIVE|LOOKUP_DIRECTORY, &nd))
error = path_walk(put_old, &nd); 在新的根盤中尋找put_old目錄
if (error) {
int blivet;

printk(KERN_NOTICE "Trying to unmount old root ... ");
blivet = do_umount(old_rootmnt, 1, 0); 卸載原始的根盤
if (!blivet) {
printk("okay/n");
return 0;
}
printk(KERN_ERR "error %d/n", blivet);
return error;
}
/* FIXME: we should hold i_zombie on nd.dentry */
move_vfsmnt(old_rootmnt, nd.dentry, nd.mnt, "/dev/root.old");
mntput(old_rootmnt);
path_release(&nd);
return 0;
}

#endif

static struct vfsmount *add_vfsmnt(struct nameidata *nd, 在虛擬文件系統中的安裝點
struct dentry *root, 安裝盤的根目錄項
const char *dev_name) 安裝盤名稱
{
struct vfsmount *mnt;
struct super_block *sb = root->d_inode->i_sb;
char *name;

mnt = kmalloc(sizeof(struct vfsmount), GFP_KERNEL);
if (!mnt)
goto out;
memset(mnt, 0, sizeof(struct vfsmount));

if (nd || dev_name)
mnt->mnt_flags = MNT_VISIBLE;

/* It may be NULL, but who cares? */
if (dev_name) { 複製所安裝安裝盤名稱字符串
name = kmalloc(strlen(dev_name)+1, GFP_KERNEL);
if (name) {
strcpy(name, dev_name);
mnt->mnt_devname = name;
}
}
mnt->mnt_owner = current->uid; 安裝盤的安裝者
atomic_set(&mnt->mnt_count,1);
mnt->mnt_sb = sb; 指向安裝盤超級塊結構

spin_lock(&dcache_lock);
if (nd && !IS_ROOT(nd->dentry) && d_unhashed(nd->dentry))
goto fail;
mnt->mnt_root = dget(root); 安裝盤的根目錄項
mnt->mnt_mountpoint = nd ? dget(nd->dentry) : dget(root); 指向上一層安裝盤的根目錄項
mnt->mnt_parent = nd ? mntget(nd->mnt) : mnt; 指向上一層安裝盤的安裝結構

if (nd) {
list_add(&mnt->mnt_child, &nd->mnt->mnt_mounts); 將安裝盤作爲上一層安裝盤的子盤
list_add(&mnt->mnt_clash, &nd->dentry->d_vfsmnt); 使安裝點目錄項指向安裝盤
} else {
INIT_LIST_HEAD(&mnt->mnt_child); 如果安裝盤是根盤
INIT_LIST_HEAD(&mnt->mnt_clash);
}
INIT_LIST_HEAD(&mnt->mnt_mounts);
list_add(&mnt->mnt_instances, &sb->s_mounts); 該安裝結構是安裝盤的一個安裝實例
list_add(&mnt->mnt_list, vfsmntlist.prev); 與所有的安裝結構相連接
spin_unlock(&dcache_lock);
out:
return mnt;
fail:
spin_unlock(&dcache_lock);
if (mnt->mnt_devname)
kfree(mnt->mnt_devname);
kfree(mnt);
return NULL;
}
static void move_vfsmnt(struct vfsmount *mnt, 源盤
struct dentry *mountpoint, 目標目錄
struct vfsmount *parent, 目標盤
const char *dev_name) 源盤名稱
{ 將mnt安裝盤以dev_name爲名稱移到parent盤的mountpoint目錄下
struct dentry *old_mountpoint;
struct vfsmount *old_parent;
char *new_devname = NULL;

if (dev_name) {
new_devname = kmalloc(strlen(dev_name)+1, GFP_KERNEL);
if (new_devname)
strcpy(new_devname, dev_name);
}

spin_lock(&dcache_lock);
old_mountpoint = mnt->mnt_mountpoint; 取源盤的父盤根目錄
old_parent = mnt->mnt_parent; 取源盤的父盤

/* flip names */
if (new_devname) {
if (mnt->mnt_devname)
kfree(mnt->mnt_devname); 釋放源盤的名稱
mnt->mnt_devname = new_devname; 改爲新的名稱
}

/* flip the linkage */
mnt->mnt_mountpoint = dget(mountpoint); 指向父盤的根目錄
mnt->mnt_parent = parent ? mntget(parent) : mnt; 指向父盤
list_del(&mnt->mnt_clash); 刪除源盤的安裝點對源盤的鏈接
list_del(&mnt->mnt_child); 刪除源盤的作爲子盤的鏈接
if (parent) {
list_add(&mnt->mnt_child, &parent->mnt_mounts); 將源盤作爲目標盤的子盤
list_add(&mnt->mnt_clash, &mountpoint->d_vfsmnt); 使目標目錄指向源盤
} else {
INIT_LIST_HEAD(&mnt->mnt_child);
INIT_LIST_HEAD(&mnt->mnt_clash);
}
spin_unlock(&dcache_lock);

/* put the old stuff */
dput(old_mountpoint);
if (old_parent != mnt)
mntput(old_parent);
}

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