open_by_handle_atシステムコール


Rev.1を表示中。最新版はこちら

force_o_largefile()で32ビットシステムならO_LARGEFILEとして、do_handle_open()でhandleのファイルIDを取得します。
SYSCALL_DEFINE3(open_by_handle_at, int, mountdirfd,
               struct file_handle __user *, handle,
               int, flags)
{
       long ret;

       if (force_o_largefile())
               flags |= O_LARGEFILE;

       ret = do_handle_open(mountdirfd, handle, flags);
       return ret;
}
handle_to_path()でファイルハンドルからpathを設定し、get_unused_fd_flags()カレントプロセスの未使用のファイルIDを取得後、handle_to_path()で取得したpath.dentryでFILE構造体を取得して、fd_install()でファイルIDに設定します。そのIDを返しことで、ユーザプロセスはこのファイルIDで通常ファイルのread/writeシステムコールで読み書きします。
long do_handle_open(int mountdirfd,
                   struct file_handle __user *ufh, int open_flag)
{
       long retval = 0;
       struct path path;
       struct file *file;
       int fd;

       retval = handle_to_path(mountdirfd, ufh, &path);
       if (retval)
               return retval;

       fd = get_unused_fd_flags(open_flag);
       if (fd < 0) {
               path_put(&path);
               return fd;
       }
       file = file_open_root(path.dentry, path.mnt, "", open_flag);
       if (IS_ERR(file)) {
               put_unused_fd(fd);
               retval =  PTR_ERR(file);
       } else {
               retval = fd;
               fsnotify_open(file);
               fd_install(fd, file);
       }
       path_put(&path);
       return retval;
}

static int handle_to_path(int mountdirfd, struct file_handle __user *ufh,
                  struct path *path)
{
       int retval = 0;
       struct file_handle f_handle;
       struct file_handle *handle = NULL;


       if (!capable(CAP_DAC_READ_SEARCH)) {
               retval = -EPERM;
               goto out_err;
       }
       if (copy_from_user(&f_handle, ufh, sizeof(struct file_handle))) {
               retval = -EFAULT;
               goto out_err;
       }
       if ((f_handle.handle_bytes > MAX_HANDLE_SZ) ||
           (f_handle.handle_bytes == 0)) {
               retval = -EINVAL;
               goto out_err;
       }
       handle = kmalloc(sizeof(struct file_handle) + f_handle.handle_bytes,
                        GFP_KERNEL);
       if (!handle) {
               retval = -ENOMEM;
               goto out_err;
       }

       if (copy_from_user(handle, ufh,
                          sizeof(struct file_handle) +
                          f_handle.handle_bytes)) {
               retval = -EFAULT;
               goto out_handle;
       }

       retval = do_handle_to_path(mountdirfd, handle, path);

out_handle:
       kfree(handle);
out_err:
       return retval;
}
get_vfsmount_from_fd()でマウント元のファイルIDのmountdirfdからvfsmount構造体を取得します。この時mountdirfd=AT_FDCWDなら、current->fs->pwdから取得し、exportfs_decode_fh()でhandle->f_handleのinode番号で、dentryを取得します。
static int do_handle_to_path(int mountdirfd, struct file_handle *handle,
                            struct path *path)
{
       int retval = 0;
       int handle_dwords;

       path->mnt = get_vfsmount_from_fd(mountdirfd);
       if (IS_ERR(path->mnt)) {
               retval = PTR_ERR(path->mnt);
               goto out_err;
       }
       /* change the handle size to multiple of sizeof(u32) */
       handle_dwords = handle->handle_bytes >> 2;
       path->dentry = exportfs_decode_fh(path->mnt,
                                         (struct fid *)handle->f_handle,
                                         handle_dwords, handle->handle_type,
                                         vfs_dentry_acceptable, NULL);
       if (IS_ERR(path->dentry)) {
               retval = PTR_ERR(path->dentry);
               goto out_mnt;
       }
       return 0;
out_mnt:
       mntput(path->mnt);
out_err:
       return retval;
}
スーパブロックのexport_operationsコールバックのfh_to_dentry()でdentryを取得します。取得したdentryがディレクトリの時、親ディレクトリ下に無ければ、reconnect_path()で親に設定します。事の時、ファイルシステムのrootまで、順次、親ディレクトリの親ディレクトリも同様にチェックします。

取得したdentryが通常ファイルなら、このdentryがアクセス可能かチェックし、OKならそのdentryを返します。取得できないなら親ディレクトリにこのdentryを登録しなければなりません。親ディレクトリを取得し、dentryがDCACHE_DISCONNECTEDされてないかチェックします。なお、この親ディレクトリも取得したdentryがディレクトリの時と同様reconnect_path()をコールします。通常ファイルがアクセスできない場合、親ディレクトリその物が無くなっている事もあるからです。

ここまでOKなら、exportfs_get_name()で親ディレクトリにこのdentryに、nop->fh_to_dentry()のdentryが登録されているかチェックし必要なら登録します。
struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid,
               int fh_len, int fileid_type,
               int (*acceptable)(void *, struct dentry *), void *context)
{
       const struct export_operations *nop = mnt->mnt_sb->s_export_op;
       struct dentry *result, *alias;
       char nbuf[NAME_MAX+1];
       int err;


       if (!nop || !nop->fh_to_dentry)
               return ERR_PTR(-ESTALE);
       result = nop->fh_to_dentry(mnt->mnt_sb, fid, fh_len, fileid_type);
       if (!result)
               result = ERR_PTR(-ESTALE);
       if (IS_ERR(result))
               return result;

       if (S_ISDIR(result->d_inode->i_mode)) {

               if (result->d_flags & DCACHE_DISCONNECTED) {
                       err = reconnect_path(mnt, result, nbuf);
                       if (err)
                               goto err_result;
               }

               if (!acceptable(context, result)) {
                       err = -EACCES;
                       goto err_result;
               }

               return result;
       } else {
               struct dentry *target_dir, *nresult;

               alias = find_acceptable_alias(result, acceptable, context);
               if (alias)
                       return alias;

               err = -ESTALE;
               if (!nop->fh_to_parent)
                       goto err_result;

               target_dir = nop->fh_to_parent(mnt->mnt_sb, fid,
                               fh_len, fileid_type);
               if (!target_dir)
                       goto err_result;
               err = PTR_ERR(target_dir);
               if (IS_ERR(target_dir))
                       goto err_result;

               err = reconnect_path(mnt, target_dir, nbuf);
               if (err) {
                       dput(target_dir);
                       goto err_result;
               }

               err = exportfs_get_name(mnt, target_dir, nbuf, result);
               if (!err) {
                       mutex_lock(&target_dir->d_inode->i_mutex);
                       nresult = lookup_one_len(nbuf, target_dir,
                                                strlen(nbuf));
                       mutex_unlock(&target_dir->d_inode->i_mutex);
                       if (!IS_ERR(nresult)) {
                               if (nresult->d_inode) {
                                       dput(result);
                                       result = nresult;
                               } else
                                       dput(nresult);
                       }
               }

               dput(target_dir);

               alias = find_acceptable_alias(result, acceptable, context);
               if (!alias) {
                       err = -EACCES;
                       goto err_result;
               }

               return alias;
       }

err_result:
       dput(result);
       return ERR_PTR(err);
}
ext2のexport_operations の.fh_to_dentry コールバック関数です。引数のext2_nfs_get_inodeは、ファイルシステムに依存したスーパブロックからinodeを取得する関数ポインターです。
static struct dentry *ext2_fh_to_dentry(struct super_block *sb, struct fid *fid,
               int fh_len, int fh_type)
{
       return generic_fh_to_dentry(sb, fid, fh_len, fh_type,
                                   ext2_nfs_get_inode);
}
get_inode()でスーパブロックからfhandleのinoに一致するinodeを検索し、inodeを取得します。get_inode()はファイルシステムに依存し、ext2ではext2_nfs_get_inode()がコールされ、inoに一致するinode取得します。

inodeが取得できたら、d_obtain_alias()でそのinodeのdentryを取得します。d_obtain_alias()で新規にdentryを割り当てる場合、inode->i_dentryに割り当てたdentryがリストされますが、別名としてdentry.d_aliasがリストされ、nfs等で割り当てられたdentryと通常のdentryを管理しています。
struct dentry *generic_fh_to_dentry(struct super_block *sb, struct fid *fid,
               int fh_len, int fh_type, struct inode *(*get_inode)
                       (struct super_block *sb, u64 ino, u32 gen))
{
       struct inode *inode = NULL;

       if (fh_len < 2)
               return NULL;

       switch (fh_type) {
       case FILEID_INO32_GEN:
       case FILEID_INO32_GEN_PARENT:
               inode = get_inode(sb, fid->i32.ino, fid->i32.gen);
               break;
       }

       return d_obtain_alias(inode);
}

static struct inode *ext2_nfs_get_inode(struct super_block *sb,
               u64 ino, u32 generation)
{
       struct inode *inode;

       if (ino < EXT2_FIRST_INO(sb) && ino != EXT2_ROOT_INO)
               return ERR_PTR(-ESTALE);
       if (ino > le32_to_cpu(EXT2_SB(sb)->s_es->s_inodes_count))
               return ERR_PTR(-ESTALE);

       inode = ext2_iget(sb, ino);
       if (IS_ERR(inode))
               return ERR_CAST(inode);
       if (generation && inode->i_generation != generation) {
               iput(inode);
               return ERR_PTR(-ESTALE);
       }
       return inode;
}
iget_locked()でスーパブロックからinoに一致するinodeを返します。もし一致するinodeがなければ新規にinodeを割り当てます。新規にinodeを割り当てたらinode->i_state & I_NEWとなり、この時、元のinode情報/コールバック関数が設定されます。

le16_to_cpu()エンディアン処理で、実デバイスデータはリトルエンディアンです。
struct inode *ext2_iget (struct super_block *sb, unsigned long ino)
{
       struct ext2_inode_info *ei;
       struct buffer_head * bh;
       struct ext2_inode *raw_inode;
       struct inode *inode;
       long ret = -EIO;
       int n;

       inode = iget_locked(sb, ino);
       if (!inode)
               return ERR_PTR(-ENOMEM);
       if (!(inode->i_state & I_NEW))
               return inode;

       ei = EXT2_I(inode);
       ei->i_block_alloc_info = NULL;

       raw_inode = ext2_get_inode(inode->i_sb, ino, &bh);
       if (IS_ERR(raw_inode)) {
               ret = PTR_ERR(raw_inode);
               goto bad_inode;
       }

       inode->i_mode = le16_to_cpu(raw_inode->i_mode);
       inode->i_uid = (uid_t)le16_to_cpu(raw_inode->i_uid_low);
       inode->i_gid = (gid_t)le16_to_cpu(raw_inode->i_gid_low);
       if (!(test_opt (inode->i_sb, NO_UID32))) {
               inode->i_uid |= le16_to_cpu(raw_inode->i_uid_high) << 16;
               inode->i_gid |= le16_to_cpu(raw_inode->i_gid_high) << 16;
       }
       set_nlink(inode, le16_to_cpu(raw_inode->i_links_count));
       inode->i_size = le32_to_cpu(raw_inode->i_size);
       inode->i_atime.tv_sec = (signed)le32_to_cpu(raw_inode->i_atime);
       inode->i_ctime.tv_sec = (signed)le32_to_cpu(raw_inode->i_ctime);
       inode->i_mtime.tv_sec = (signed)le32_to_cpu(raw_inode->i_mtime);
       inode->i_atime.tv_nsec = inode->i_mtime.tv_nsec = inode->i_ctime.tv_nsec = 0;
       ei->i_dtime = le32_to_cpu(raw_inode->i_dtime);

       if (inode->i_nlink == 0 && (inode->i_mode == 0 || ei->i_dtime)) {
               brelse (bh);
               ret = -ESTALE;
               goto bad_inode;
       }
       inode->i_blocks = le32_to_cpu(raw_inode->i_blocks);
       ei->i_flags = le32_to_cpu(raw_inode->i_flags);
       ei->i_faddr = le32_to_cpu(raw_inode->i_faddr);
       ei->i_frag_no = raw_inode->i_frag;
       ei->i_frag_size = raw_inode->i_fsize;
       ei->i_file_acl = le32_to_cpu(raw_inode->i_file_acl);
       ei->i_dir_acl = 0;
       if (S_ISREG(inode->i_mode))
               inode->i_size |= ((__u64)le32_to_cpu(raw_inode->i_size_high)) << 32;
       else
               ei->i_dir_acl = le32_to_cpu(raw_inode->i_dir_acl);
       ei->i_dtime = 0;
       inode->i_generation = le32_to_cpu(raw_inode->i_generation);
       ei->i_state = 0;
       ei->i_block_group = (ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb);
       ei->i_dir_start_lookup = 0;

       for (n = 0; n < EXT2_N_BLOCKS; n++)
               ei->i_data[n] = raw_inode->i_block[n];

       if (S_ISREG(inode->i_mode)) {
               inode->i_op = &ext2_file_inode_operations;
               if (ext2_use_xip(inode->i_sb)) {
                       inode->i_mapping->a_ops = &ext2_aops_xip;
                       inode->i_fop = &ext2_xip_file_operations;
               } else if (test_opt(inode->i_sb, NOBH)) {
                       inode->i_mapping->a_ops = &ext2_nobh_aops;
                       inode->i_fop = &ext2_file_operations;
               } else {
                       inode->i_mapping->a_ops = &ext2_aops;
                       inode->i_fop = &ext2_file_operations;
               }
       } else if (S_ISDIR(inode->i_mode)) {
               inode->i_op = &ext2_dir_inode_operations;
               inode->i_fop = &ext2_dir_operations;
               if (test_opt(inode->i_sb, NOBH))
                       inode->i_mapping->a_ops = &ext2_nobh_aops;
               else
                       inode->i_mapping->a_ops = &ext2_aops;
       } else if (S_ISLNK(inode->i_mode)) {
               if (ext2_inode_is_fast_symlink(inode)) {
                       inode->i_op = &ext2_fast_symlink_inode_operations;
                       nd_terminate_link(ei->i_data, inode->i_size,
                               sizeof(ei->i_data) - 1);
               } else {
                       inode->i_op = &ext2_symlink_inode_operations;
                       if (test_opt(inode->i_sb, NOBH))
                               inode->i_mapping->a_ops = &ext2_nobh_aops;
                       else
                               inode->i_mapping->a_ops = &ext2_aops;
               }
       } else {
               inode->i_op = &ext2_special_inode_operations;
               if (raw_inode->i_block[0])
                       init_special_inode(inode, inode->i_mode,
                          old_decode_dev(le32_to_cpu(raw_inode->i_block[0])));
               else 
                       init_special_inode(inode, inode->i_mode,
                          new_decode_dev(le32_to_cpu(raw_inode->i_block[1])));
       }
       brelse (bh);
       ext2_set_inode_flags(inode);
       unlock_new_inode(inode);
       return inode;
       
bad_inode:
       iget_failed(inode);
       return ERR_PTR(ret);
}

最終更新 2014/05/09 01:26:16 - north
(2014/05/09 01:26:16 作成)


検索

アクセス数
3586032
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。