Linux内核2.4.18创建硬链接的系统调用sys_link

1、磁盘目录项结构
/*
 * The new version of the directory entry.  Since EXT2 structures are
 * stored in intel byte order, and the name_len field could never be
 * bigger than 255 chars, it's safe to reclaim the extra byte for the
 * file_type field.
 */
struct ext2_dir_entry_2 {
    __u32    inode;            /* Inode number */
    __u16    rec_len;        /* Directory entry length */
    __u8    name_len;        /* Name length */
    __u8    file_type;
    char    name[EXT2_NAME_LEN];    /* File name */
};
inode:inode节点号
rec_len:目录项长度
name_len:实际的目录项名称长度(一般不等于EXT2_NAME_LEN)
file_type:文件类型编码
name:目录项名称(不含'\0')


2、调用路径:sys_link->vfs_link->ext2_link->ext2_add_nondir->ext2_add_link


3、函数分析

/*
 * Hardlinks are often used in delicate situations.  We avoid
 * security-related surprises by not following symlinks on the
 * newname.  --KAB
 *
 * We don't follow them on the oldname either to be compatible
 * with linux 2.0, and to avoid hard-linking to directories
 * and other special files.  --ADM
 */
asmlinkage long sys_link(const char * oldname, const char * newname)
{
    int error;
    char * from;
    char * to;

    from = getname(oldname);
    if(IS_ERR(from))
        return PTR_ERR(from);
    to = getname(newname);
    error = PTR_ERR(to);
    if (!IS_ERR(to)) {
        struct dentry *new_dentry;
        struct nameidata nd, old_nd;

        error = 0;
        if (path_init(from, LOOKUP_POSITIVE, &old_nd))
            error = path_walk(from, &old_nd);
        if (error)
            goto exit;
        if (path_init(to, LOOKUP_PARENT, &nd))
            error = path_walk(to, &nd);
        if (error)
            goto out;
        error = -EXDEV;
        if (old_nd.mnt != nd.mnt)
            goto out_release;
        new_dentry = lookup_create(&nd, 0);
        error = PTR_ERR(new_dentry);
        if (!IS_ERR(new_dentry)) {
            error = vfs_link(old_nd.dentry, nd.dentry->d_inode, new_dentry);
            dput(new_dentry);
        }
        up(&nd.dentry->d_inode->i_sem);
out_release:
        path_release(&nd);
out:
        path_release(&old_nd);
exit:
        putname(to);
    }
    putname(from);

    return error;
}
//作为系统调用API和指定文件系统层的接口,处于VFS层。
int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
{
    struct inode *inode;
    int error;

    down(&dir->i_zombie);
    error = -ENOENT;
    inode = old_dentry->d_inode;
    if (!inode)
        goto exit_lock;

    error = may_create(dir, new_dentry);//检查是否具备创建权限
    if (error)
        goto exit_lock;

    error = -EXDEV;
    if (dir->i_dev != inode->i_dev) //硬链接不能跨设备,跨文件系统建立。
        goto exit_lock;

    /*
     * A link to an append-only or immutable file cannot be created.
     */
    error = -EPERM;
    if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
        goto exit_lock;
    if (!dir->i_op || !dir->i_op->link)
        goto exit_lock;

    DQUOT_INIT(dir);
    lock_kernel();
    error = dir->i_op->link(old_dentry, dir, new_dentry);//调用ext2_link
    unlock_kernel();

exit_lock:
    up(&dir->i_zombie);
    if (!error)
        inode_dir_notify(dir, DN_CREATE);
    return error;
}

//ext2文件系统的创建链接函数,调用ext2_add_nondir。
static int ext2_link (struct dentry * old_dentry, struct inode * dir,
    struct dentry *dentry)
{
    struct inode *inode = old_dentry->d_inode;

    if (S_ISDIR(inode->i_mode)) //不能创建指向目录节点的硬链接
        return -EPERM;

    if (inode->i_nlink >= EXT2_LINK_MAX)
        return -EMLINK;

    inode->i_ctime = CURRENT_TIME;
    ext2_inc_count(inode);
    atomic_inc(&inode->i_count);

    return ext2_add_nondir(dentry, inode);
}

//在dentry->parent目录中创建指向节点inode(节点号inode->i_ino)的链接,并将对应的内存目录项dentry和inode建立关联
static inline int ext2_add_nondir(struct dentry *dentry, struct inode *inode)
{
    int err = ext2_add_link(dentry, inode);
    if (!err) {
        d_instantiate(dentry, inode);
        return 0;
    }
    ext2_dec_count(inode);
    iput(inode);
    return err;
}


/*
 *    Parent is locked.
 */
int ext2_add_link (struct dentry *dentry, struct inode *inode)
{
    struct inode *dir = dentry->d_parent->d_inode;
    const char *name = dentry->d_name.name;
    int namelen = dentry->d_name.len;
    unsigned reclen = EXT2_DIR_REC_LEN(namelen);//根据创建目录项名称长度计算出目录项长度reclen
    unsigned short rec_len, name_len;
    struct page *page = NULL;
    ext2_dirent * de;
    unsigned long npages = dir_pages(dir);//根据目录长度,推算出目录文件的页面数
    unsigned long n;
    char *kaddr;
    unsigned from, to;
    int err;

    /* We take care of directory expansion in the same loop */
    for (n = 0; n <= npages; n++) {//循环检测每一页,寻找目录项空位
        page = ext2_get_page(dir, n);
        err = PTR_ERR(page);
        if (IS_ERR(page))
            goto out;
        kaddr = page_address(page);//本页起始地址:页结构指针转换为线性地址
        de = (ext2_dirent *)kaddr;//当前目录项地址
        kaddr += PAGE_CACHE_SIZE - reclen;//kaddr=本页最后一个目录项地址(页起始地址+页长-新目录项长度)
        while ((char *)de <= kaddr) { //页内循环检测每个目录项,一旦下个检测地址高于kaddr,那么说明本页没有适合的位置,必须看下页
            err = -EEXIST;
            if (ext2_match (namelen, name, de))//目录中存在同名的目录项,则退出,返回-EEXIST
                goto out_page;
            name_len = EXT2_DIR_REC_LEN(de->name_len);//name_len=当前目录项需要的长度
            rec_len = le16_to_cpu(de->rec_len); //rec_len=当前目录项实际占用的长度
            if (!de->inode && rec_len >= reclen)//情况1:如果当前目录项空闲,且长度合适,那么可以放置新的目录项,转向got_it。
                goto got_it;
            if (rec_len >= name_len + reclen)//情况2:如果当前目录项长度>=当前目录项需要长度+新目录项需要长度,那么可以从中分出尾部部分放置新目录项
                goto got_it;
            de = (ext2_dirent *) ((char *) de + rec_len);//否则看下个目录项
        }
        ext2_put_page(page);
    }
    BUG();
    return -EINVAL;

got_it:
    from = (char*)de - (char*)page_address(page);
    to = from + rec_len;
    lock_page(page);
    err = page->mapping->a_ops->prepare_write(NULL, page, from, to);
    if (err)
        goto out_unlock;
    if (de->inode) {//针对情况2
        ext2_dirent *de1 = (ext2_dirent *) ((char *) de + name_len);//新目录项的放置位置de1在当前目录项后边,即当前目录项位置de+当前目录项实际需要的长度name_len
        de1->rec_len = cpu_to_le16(rec_len - name_len);//新目录项的实际占用长度de1->rec_len=当前目录项实际占用长度rec_len - 当前目录项需要的长度name_len
        de->rec_len = cpu_to_le16(name_len);//修改当前目录项实际占用长度rec_len为需要的长度name_len
        de = de1;//de指向新目录项的位置
    }
    de->name_len = namelen;
    memcpy (de->name, name, namelen);//拷贝目录项名称字符串
    de->inode = cpu_to_le32(inode->i_ino);//关联i节点号
    ext2_set_de_type (de, inode);
    err = ext2_commit_chunk(page, from, to);//提交页面的修改部分(相关bh置为脏),如果块设备有MS_SYNCHRONOUS标志或者目录文件有S_SYNC标志,则立刻同步的磁盘。
    dir->i_mtime = dir->i_ctime = CURRENT_TIME;//修改目录节点的时间戳。
    mark_inode_dirty(dir);//目录文件节点置为脏
    /* OFFSET_CACHE */
out_unlock:
    UnlockPage(page);
out_page:
    ext2_put_page(page);
out:
    return err;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据提供的引用内容,我们可以使用以下方法来查看Linux系统中的root密码: 方法一: 1. 在系统进入单用户状态后,直接使用命令passwd root来更改root密码。 方法二: 1. 如果系统无法访问,可以使用安装光盘引导系统,进入linux rescue状态。 2. 将原来的/分区挂载上来,可以使用以下命令: cd /mnt mkdir hd mount -t auto /dev/hdaX(原来/分区所在的分区号) hd cd hd chroot ./ 3. 使用命令passwd root来更改root密码。 方法三: 1. 将本机的硬盘拿下来,挂到其他的Linux系统上,使用与方法二相同的步骤来更改root密码。 方法四: 1. 在LILO引导系统时,在lilo提示符出现时,键入"linux single"进入单用户模式。 2. 使用命令"vi /etc/shadow"编辑/etc/shadow文件,将以root开头的一行中的root:后和下一个:前的内容删除,并保存。 方法五: 1. 在GRUB引导系统时,在grub画面出现时,使用上下键选中平时启动Linux的选项,然后按e键。 2. 修改命令行,在kernel后加入"single",例如:"kernel /boot/vmlinuz-2.4.18-14 ro root=LABEL=/ single",然后按b键启动进入单用户模式。 3. 使用命令"vi /etc/shadow"编辑/etc/shadow文件,将以root开头的一行中的root:后和下一个:前的内容删除,并保存。 请注意,以上提供的方法仅用于找回Linux系统中的root密码,使用时请遵循相关安全规范和法律法规。<span class="em">1</span><span class="em">2</span> #### 引用[.reference_title] - *1* [忘记 linux root 密码](https://blog.csdn.net/junjun5156/article/details/53700678)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* [Linux找回root用户密码【亲测有效】](https://blog.csdn.net/Gherbirthday0916/article/details/126830335)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值