getdentsシステムコール


readdir()でlsコマンドを作成できます。
#include <stdio.h>
#include <sys/stat.h>
#include <dirent.h>

int main ()
{
   struct dirent *dirst;
   int     i =1;

   DIR *dp = opendir("/");
   while((dirst = readdir(dp)) != NULL)
   {
       if (!(i++ % 8)) {
               printf("\n");
       }
       printf("%11s", dirst->d_name);
   }
   closedir(dp);
   printf("\n");
   return 0;
}

[root@localhost kitamura]# ./a.out
       bin       home          .         ..    debugfs        opt       proc
lost+found        var       boot        mnt     pstore       mnt1       sbin        tmp
       dev        sys        etc         va        usr        run         sd       root
   sysroot        srv.autorelabel      media        lib
ちなみにstruct direntは下記のように定義されています。これはext2/3等のファイルシステムのディレクトフォーマットに準じての宣言です。
struct dirent {
   ino_t          d_ino;       /* inode 番号 */
   off_t          d_off;       /* オフセットではない; 注意を参照 */
   unsigned short d_reclen;    /* このレコードの長さ */
   unsigned char  d_type;      /* ファイル種別。全ファイルシステム */
                                  でサポートされているわけではない */
   char           d_name[256]; /* ファイル名 */
};
readdir()はシステムコールgetdentsがコールされます。引数は順にディレクトリのファイルID、バッファ、バッファサイズです。ディレクトリの読み出しは、ファイルの読み出しと同じようにデータブロック領域の読み出しを行うわけですが、ファイルと違って、inodeディレクトリのフォーマットとして読み込む必要があります。それ故ユーザ引数のバッファはdirentとなっている訳です。
SYSCALL_DEFINE3(getdents, unsigned int, fd,
               struct linux_dirent __user *, dirent, unsigned int, count)
{
       struct file * file;
       struct linux_dirent __user * lastdirent;
       struct getdents_callback buf;
       int error;

       error = -EFAULT;
       if (!access_ok(VERIFY_WRITE, dirent, count))
               goto out;

       error = -EBADF;
       file = fget(fd);
       if (!file)
               goto out;

       buf.current_dir = dirent;
       buf.previous = NULL;
       buf.count = count;
       buf.error = 0;

       error = vfs_readdir(file, filldir, &buf);
       if (error >= 0)
               error = buf.error;
       lastdirent = buf.previous;
       if (lastdirent) {
               if (put_user(file->f_pos, &lastdirent->d_off))
                       error = -EFAULT;
               else
                       error = count - buf.count;
       }
       fput(file);
out:
       return error;
}
vfs_readdir()の第2引数fillerは、ファイルシステムのファーマットに依存するinodeデータブロックに有するディレクトリ情報を、buffに設定するコールバック関数です。初期チェック後、ファイルオペレーションのreaddirをコールします。
int vfs_readdir(struct file *file, filldir_t filler, void *buf)
{
       struct inode *inode = file->f_path.dentry->d_inode;
       int res = -ENOTDIR;
       if (!file->f_op || !file->f_op->readdir)
               goto out;

       res = security_file_permission(file, MAY_READ);
       if (res)
               goto out;

       res = mutex_lock_killable(&inode->i_mutex);
       if (res)
               goto out;

       res = -ENOENT;
       if (!IS_DEADDIR(inode)) {
               res = file->f_op->readdir(file, buf, filler);
               file_accessed(file);
       }
       mutex_unlock(&inode->i_mutex);
out:
       return res;
}
dcache_readdir()はファイルオペレーション simple_dir_operationsの.readdirコールバック関数です。simple_dir_operationsはramfsのような実デバイスを有しないファイルシステムで使われています。

.、..そして処理対象のdentryの子dentryを操作し、その内容をfilldir()でdirentに設定します。子dentryを操作では、いったん子のdentry->d_subdirsのdentryをcursor->d_u.d_childに移して、そのリストを操作しています。リスト処理をロック等の処理をできるだけ介在することなく行うためだと思います。)

filp->f_posはバッファー内のオフセットですが、ディレクトリの操作ではインデックスとなり、ユーザプロセス側では、返り値のオフセットで、取得できた数として処理します。

なお、.、..ディレクトリは仮想ディレクトリ(実デバイスはもちろん、vsf下においてもdentryとして存在していない。)で、inode番号は、親のinode番号となります。
int dcache_readdir(struct file * filp, void * dirent, filldir_t filldir)
{
       struct dentry *dentry = filp->f_path.dentry;
       struct dentry *cursor = filp->private_data;
       struct list_head *p, *q = &cursor->d_u.d_child;
       ino_t ino;
       int i = filp->f_pos;

       switch (i) {
               case 0:
                       ino = dentry->d_inode->i_ino;
                       if (filldir(dirent, ".", 1, i, ino, DT_DIR) < 0)
                               break;
                       filp->f_pos++;
                       i++;
               case 1:
                       ino = parent_ino(dentry);
                       if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0)
                               break;
                       filp->f_pos++;
                       i++;
               default:
                       spin_lock(&dentry->d_lock);
                       if (filp->f_pos == 2)
                               list_move(q, &dentry->d_subdirs);

                       for (p=q->next; p != &dentry->d_subdirs; p=p->next) {
                               struct dentry *next;
                               next = list_entry(p, struct dentry, d_u.d_child);
                               spin_lock_nested(&next->d_lock, DENTRY_D_LOCK_NESTED);
                               if (!simple_positive(next)) {
                                       spin_unlock(&next->d_lock);
                                       continue;
                               }

                               spin_unlock(&next->d_lock);
                               spin_unlock(&dentry->d_lock);
                               if (filldir(dirent, next->d_name.name, 
                                           next->d_name.len, filp->f_pos, 
                                           next->d_inode->i_ino, 
                                           dt_type(next->d_inode)) < 0)
                                       return 0;
                               spin_lock(&dentry->d_lock);
                               spin_lock_nested(&next->d_lock, DENTRY_D_LOCK_NESTED);
                               /* next is still alive */
                               list_move(q, p);
                               spin_unlock(&next->d_lock);
                               p = q;
                               filp->f_pos++;
                       }
                       spin_unlock(&dentry->d_lock);
       }
       return 0;
}
filldir()でディレクトリ配下の内容をユーザバッファに設定します。引数は順に、ユーザバッファ、ディレクトリ名、ディレクトリ名長、取得するインデックス、inode属性。ただし、実際のユーザバッファは、buf->current_dirとなります。(sys_getdentsでbuf.current_dir = direntとしています。)

dirent = buf->current_dir()とし、
dirent->d_off/dirent->d_ino/dirent->d_reclen/dirent->d_nameと最後の位置にinode属性を設定し、buf->current_dir = (void __user *)dirent + reclenで次の設定に備えます。
static int filldir(void * __buf, const char * name, int namlen, loff_t offset,
                  u64 ino, unsigned int d_type)
{
       struct linux_dirent __user * dirent;
       struct getdents_callback * buf = (struct getdents_callback *) __buf;
       unsigned long d_ino;
       int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2,
               sizeof(long));

       buf->error = -EINVAL;   /* only used if we fail.. */
       if (reclen > buf->count)
               return -EINVAL;
       d_ino = ino;
       if (sizeof(d_ino) < sizeof(ino) && d_ino != ino) {
               buf->error = -EOVERFLOW;
               return -EOVERFLOW;
       }
       dirent = buf->previous;
       if (dirent) {
               if (__put_user(offset, &dirent->d_off))
                       goto efault;
       }
       dirent = buf->current_dir;
       if (__put_user(d_ino, &dirent->d_ino))
               goto efault;
       if (__put_user(reclen, &dirent->d_reclen))
               goto efault;
       if (copy_to_user(dirent->d_name, name, namlen))
               goto efault;
       if (__put_user(0, dirent->d_name + namlen))
               goto efault;
       if (__put_user(d_type, (char __user *) dirent + reclen - 1))
               goto efault;
       buf->previous = dirent;
       dirent = (void __user *)dirent + reclen;
       buf->current_dir = dirent;
       buf->count -= reclen;
       return 0;
efault:
       buf->error = -EFAULT;
       return -EFAULT;
}


最終更新 2013/07/26 18:19:36 - north
(2013/07/26 17:52:54 作成)


検索

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