前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >minix文件系统源码分析之namei.c(上)(基于linux1.2.13)

minix文件系统源码分析之namei.c(上)(基于linux1.2.13)

作者头像
theanarkh
发布2019-07-30 18:32:57
9630
发布2019-07-30 18:32:57
举报
文章被收录于专栏:原创分享原创分享原创分享
/*
 *  linux/fs/minix/namei.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 */

#ifdef MODULE
#include <linux/module.h>
#endif

#include <linux/sched.h>
#include <linux/minix_fs.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
#include <linux/errno.h>

#include <asm/segment.h>

/*
 * comment out this line if you want names > info->s_namelen chars to be
 * truncated. Else they will be disallowed (ENAMETOOLONG).
 */
/* #define NO_TRUNCATE */

static inline int namecompare(int len, int maxlen,
  const char * name, const char * buffer)
{
  if (len > maxlen)
    return 0;
  if (len < maxlen && buffer[len])
    return 0;
  return !memcmp(name, buffer, len);
}

/*
 * ok, we cannot use strncmp, as the name is not in our data space.
 * Thus we'll have to use minix_match. No big problem. Match also makes
 * some sanity tests.
 *
 * NOTE! unlike strncmp, minix_match returns 1 for success, 0 for failure.
 */
// 比较文件名是否相等并且更新下一个要比较的地址偏移
static int minix_match(int len, const char * name,
  struct buffer_head * bh, unsigned long * offset,
  struct minix_sb_info * info)
{
  struct minix_dir_entry * de;
  // 目录项
  de = (struct minix_dir_entry *) (bh->b_data + *offset);
  // 指向下一个目录项,s_dirsize表示一个目录项的大小
  *offset += info->s_dirsize;
  // 参数校验,s_namelen是该文件系统对文件名的长度限制
  if (!de->inode || len > info->s_namelen)
    return 0;
  /* "" means "." ---> so paths like "/usr/lib//libc.a" work */
  // name是空并且当前目录项的名字是当前目录,则返回匹配,为了解决路径中连续两个斜杠的问题
  if (!len && (de->name[0]=='.') && (de->name[1]=='\0'))
    return 1;
  // 比较名字
  return namecompare(len,info->s_namelen,name,de->name);
}

/*
 *  minix_find_entry()
 *
 * finds an entry in the specified directory with the wanted name. It
 * returns the cache buffer in which the entry was found, and the entry
 * itself (as a parameter - res_dir). It does NOT read the inode of the
 * entry - you'll have to do that yourself if you want to.
 */
// 根据指定目录返回该目录下文件名为name的entry结构
static struct buffer_head * minix_find_entry(struct inode * dir,
  const char * name, int namelen, struct minix_dir_entry ** res_dir)
{
  unsigned long block, offset;
  struct buffer_head * bh;
  struct minix_sb_info * info;

  *res_dir = NULL;
  if (!dir || !dir->i_sb)
    return NULL;
  // minix超级块
  info = &dir->i_sb->u.minix_sb;
  // 文件名长度是否超过了限制
  if (namelen > info->s_namelen) {
#ifdef NO_TRUNCATE
    return NULL;
#else
    namelen = info->s_namelen;
#endif
  }
  bh = NULL;
  block = offset = 0;
  // 有没有超过了目录文件的大小
  while (block*BLOCK_SIZE+offset < dir->i_size) {
    if (!bh) {
      // 读入目录的内容,即一系列的目录项结构体
      bh = minix_bread(dir,block,0);
      // 读失败则跳过,继续读下一块
      if (!bh) {
        block++;
        continue;
      }
    }
    // 保存当前要比较的目录项,返回给调用方,offset在minix_match中被更新为下一个目录项
    *res_dir = (struct minix_dir_entry *) (bh->b_data + offset);
    // 匹配则返回
    if (minix_match(namelen,name,bh,&offset,info))
      return bh;
    // 不匹配,判断是否超过了数据块的大小,不是的话继续匹配下一个目录项
    if (offset < bh->b_size)
      continue;
    // 该块目录项已经比较完,还没有找到合适的
    brelse(bh);
    // 重置buffer,下一个循环读入下一个数据块
    bh = NULL;
    // 重置目录项偏移
    offset = 0;
    // 下一个循环要读取的块号
    block++;
  }
  brelse(bh);
  *res_dir = NULL;
  return NULL;
}
// 查找name对应的inode内容
int minix_lookup(struct inode * dir,const char * name, int len,
  struct inode ** result)
{
  int ino;
  struct minix_dir_entry * de;
  struct buffer_head * bh;

  *result = NULL;
  if (!dir)
    return -ENOENT;
  if (!S_ISDIR(dir->i_mode)) {
    iput(dir);
    return -ENOENT;
  }
  // 判断是否有name对应的目录项
  if (!(bh = minix_find_entry(dir,name,len,&de))) {
    iput(dir);
    return -ENOENT;
  }
  // inode号
  ino = de->inode;
  brelse(bh);
  // 读取inode的内容
  if (!(*result = iget(dir->i_sb,ino))) {
    iput(dir);
    return -EACCES;
  }
  iput(dir);
  return 0;
}

/*
 *  minix_add_entry()
 *
 * adds a file entry to the specified directory, returning a possible
 * error value if it fails.
 *
 * NOTE!! The inode part of 'de' is left at 0 - which means you
 * may not sleep between calling this and putting something into
 * the entry, as someone else might have used it while you slept.
 */
// 新增一个目录项
static int minix_add_entry(struct inode * dir,
  const char * name, int namelen,
  struct buffer_head ** res_buf,
  struct minix_dir_entry ** res_dir)
{
  int i;
  unsigned long block, offset;
  struct buffer_head * bh;
  struct minix_dir_entry * de;
  struct minix_sb_info * info;

  *res_buf = NULL;
  *res_dir = NULL;
  if (!dir || !dir->i_sb)
    return -ENOENT;
  // 超级块内容
  info = &dir->i_sb->u.minix_sb;
  if (namelen > info->s_namelen) {
#ifdef NO_TRUNCATE
    return -ENAMETOOLONG;
#else
    namelen = info->s_namelen;
#endif
  }
  if (!namelen)
    return -ENOENT;
  bh = NULL;
  block = offset = 0;
  while (1) {
    if (!bh) {
      bh = minix_bread(dir,block,1);
      if (!bh)
        return -ENOSPC;
    }
    // 当前目录项
    de = (struct minix_dir_entry *) (bh->b_data + offset);
    // 更新下一个目录项地址
    offset += info->s_dirsize;
    // 遍历完全部的目录项,都没有找到重复的项,则追加一个目录项
    if (block*bh->b_size + offset > dir->i_size) {
      // 初始化
      de->inode = 0;
      // 更新目录文件大小
      dir->i_size = block*bh->b_size + offset;
      // inode节点数据有更新,需要回写
      dir->i_dirt = 1;
    }
    // 已存在的目录项
    if (de->inode) {
      // 判断文件名为name的目录项是否已经存在
      if (namecompare(namelen, info->s_namelen, name, de->name)) {
        brelse(bh);
        return -EEXIST;
      }
    } else {
      // 新增一个目录项
      dir->i_mtime = dir->i_ctime = CURRENT_TIME;
      dir->i_dirt = 1;
      for (i = 0; i < info->s_namelen ; i++)
        de->name[i] = (i < namelen) ? name[i] : 0;
      dir->i_version = ++event;
      // 新增了一个目录项,需要回写
      mark_buffer_dirty(bh, 1);
      // 返回新增的目录项
      *res_dir = de;
      break;
    }
    // 还没超过块的大小,则不需要读入新的块
    if (offset < bh->b_size)
      continue;
    brelse(bh);
    // 重置然后读入下一块
    bh = NULL;
    offset = 0;
    block++;
  }
  *res_buf = bh;
  return 0;
}
// 新建一个文件
int minix_create(struct inode * dir,const char * name, int len, int mode,
  struct inode ** result)
{
  int error;
  struct inode * inode;
  struct buffer_head * bh;
  struct minix_dir_entry * de;

  *result = NULL;
  if (!dir)
    return -ENOENT;
  // 在硬盘和内存都新建一个inode节点
  inode = minix_new_inode(dir);
  if (!inode) {
    iput(dir);
    return -ENOSPC;
  }
  inode->i_op = &minix_file_inode_operations;
  inode->i_mode = mode;
  inode->i_dirt = 1;
  // 找到一个可用的目录项,de保存目录项地址
  error = minix_add_entry(dir,name,len, &bh ,&de);
  if (error) {
    inode->i_nlink--;
    inode->i_dirt = 1;
    iput(inode);
    iput(dir);
    return error;
  }
  // 保存inode号到目录项
  de->inode = inode->i_ino;
  mark_buffer_dirty(bh, 1);
  brelse(bh);
  iput(dir);
  *result = inode;
  return 0;
}
// 新建一个inode,文件类型根据mode判断
int minix_mknod(struct inode * dir, const char * name, int len, int mode, int rdev)
{
  int error;
  struct inode * inode;
  struct buffer_head * bh;
  struct minix_dir_entry * de;

  if (!dir)
    return -ENOENT;
  // 判断之前是否已经存在名字为name的目录项
  bh = minix_find_entry(dir,name,len,&de);
  if (bh) {
    brelse(bh);
    iput(dir);
    return -EEXIST;
  }
  // 新建一个inode
  inode = minix_new_inode(dir);
  if (!inode) {
    iput(dir);
    return -ENOSPC;
  }
  inode->i_uid = current->fsuid;
  inode->i_mode = mode;
  inode->i_op = NULL;
  if (S_ISREG(inode->i_mode))
    inode->i_op = &minix_file_inode_operations;
  else if (S_ISDIR(inode->i_mode)) {
    inode->i_op = &minix_dir_inode_operations;
    if (dir->i_mode & S_ISGID)
      inode->i_mode |= S_ISGID;
  }
  else if (S_ISLNK(inode->i_mode))
    inode->i_op = &minix_symlink_inode_operations;
  else if (S_ISCHR(inode->i_mode))
    inode->i_op = &chrdev_inode_operations;
  else if (S_ISBLK(inode->i_mode))
    inode->i_op = &blkdev_inode_operations;
  else if (S_ISFIFO(inode->i_mode))
    init_fifo(inode);
  if (S_ISBLK(mode) || S_ISCHR(mode))
    inode->i_rdev = rdev;
  inode->i_dirt = 1;
  // 新增一个目录项,de保存新增的地址
  error = minix_add_entry(dir, name, len, &bh, &de);
  if (error) {
    inode->i_nlink--;
    inode->i_dirt = 1;
    iput(inode);
    iput(dir);
    return error;
  }
  // 保存inode号到目录项中
  de->inode = inode->i_ino;
  mark_buffer_dirty(bh, 1);
  brelse(bh);
  iput(dir);
  iput(inode);
  return 0;
}
// 新建一个目录
int minix_mkdir(struct inode * dir, const char * name, int len, int mode)
{
  int error;
  struct inode * inode;
  struct buffer_head * bh, *dir_block;
  struct minix_dir_entry * de;
  struct minix_sb_info * info;

  if (!dir || !dir->i_sb) {
    iput(dir);
    return -EINVAL;
  }
  // 超级块
  info = &dir->i_sb->u.minix_sb;
  // 重复性判断
  bh = minix_find_entry(dir,name,len,&de);
  if (bh) {
    brelse(bh);
    iput(dir);
    return -EEXIST;
  }
  // 文件个数是否超过限制 
  if (dir->i_nlink >= MINIX_LINK_MAX) {
    iput(dir);
    return -EMLINK;
  }
  // 新增一个inode
  inode = minix_new_inode(dir);
  if (!inode) {
    iput(dir);
    return -ENOSPC;
  }
  inode->i_op = &minix_dir_inode_operations;
  // 设置文件的大小,为两个目录项,用来存..和.
  inode->i_size = 2 * info->s_dirsize;
  // 申请一个数据块,并读取第一块数据,即读入刚申请的那个块
  dir_block = minix_bread(inode,0,1);
  if (!dir_block) {
    iput(dir);
    inode->i_nlink--;
    inode->i_dirt = 1;
    iput(inode);
    return -ENOSPC;
  }
  de = (struct minix_dir_entry *) dir_block->b_data;
  // 第一项是保存.的,inode即自己
  de->inode=inode->i_ino;
  strcpy(de->name,".");
  // 第二项保存..的,即保存父目录项inode号
  de = (struct minix_dir_entry *) (dir_block->b_data + info->s_dirsize);
  de->inode = dir->i_ino;
  strcpy(de->name,"..");
  // inode本身链接数是一,.指向自己,所以是2个
  inode->i_nlink = 2;
  // 新增了东西,需要回写
  mark_buffer_dirty(dir_block, 1);
  brelse(dir_block);
  inode->i_mode = S_IFDIR | (mode & 0777 & ~current->fs->umask);
  if (dir->i_mode & S_ISGID)
    inode->i_mode |= S_ISGID;
  inode->i_dirt = 1;
  // 追加一个目录项,de保存新目录的地址
  error = minix_add_entry(dir, name, len, &bh, &de);
  if (error) {
    iput(dir);
    inode->i_nlink=0;
    iput(inode);
    return error;
  }
  // 保存inode号到目录项
  de->inode = inode->i_ino;
  mark_buffer_dirty(bh, 1);
  // 父目录链接数加一,因为子目录的..目录项指向他
  dir->i_nlink++;
  // 父inode也需要回写
  dir->i_dirt = 1;
  iput(dir);
  iput(inode);
  brelse(bh);
  return 0;
}

/*
 * routine to check that the specified directory is empty (for rmdir)
 */
// 判断目录是不是空的
static int empty_dir(struct inode * inode)
{
  unsigned int block, offset;
  struct buffer_head * bh;
  struct minix_dir_entry * de;
  struct minix_sb_info * info;

  if (!inode || !inode->i_sb)
    return 1;
  info = &inode->i_sb->u.minix_sb;
  block = 0;
  bh = NULL;
  // 偏移为两个目录项之后,从这个开始遍历全部目录项,有一个还有数据说明目录非空
  offset = 2*info->s_dirsize;
  // 目录文件的大小不是目录项的整数倍,说明数据有问题
  if (inode->i_size & (info->s_dirsize-1))
    goto bad_dir;
  // 目录文件大小小于两个目录项说明数据有问题,因为至少有.和..两项
  if (inode->i_size < offset)
    goto bad_dir;
  // 读取目录文件的第一块内容
  bh = minix_bread(inode,0,0);
  if (!bh)
    goto bad_dir;
  de = (struct minix_dir_entry *) bh->b_data;
  // 第一项没有内容或者不等于.则说明有问题
  if (!de->inode || strcmp(de->name,"."))
    goto bad_dir;
  de = (struct minix_dir_entry *) (bh->b_data + info->s_dirsize);
  // 第二项没有内容或者不等于..说明有问题
  if (!de->inode || strcmp(de->name,".."))
    goto bad_dir;
  // 遍历每个目录项
  while (block*BLOCK_SIZE+offset < inode->i_size) {
    if (!bh) {
      // 读入对应块的内容
      bh = minix_bread(inode,block,0);
      // 失败的就跳过
      if (!bh) {
        block++;
        continue;
      }
    }
    de = (struct minix_dir_entry *) (bh->b_data + offset);
    // 下一个目录项地址
    offset += info->s_dirsize;
    // 还有数据说明,说明非空,直接返回
    if (de->inode) {
      brelse(bh);
      return 0;
    }
    // 当前块的数据还没有读完
    if (offset < bh->b_size)
      continue;
    // 继续读下一块
    brelse(bh);
    bh = NULL;
    offset = 0;
    block++;
  }
  brelse(bh);
  return 1;
bad_dir:
  brelse(bh);
  printk("Bad directory on device %04x\n",inode->i_dev);
  return 1;
}
// 删除目录
int minix_rmdir(struct inode * dir, const char * name, int len)
{
  int retval;
  struct inode * inode;
  struct buffer_head * bh;
  struct minix_dir_entry * de;

  inode = NULL;
  // name对应的目录是否存在
  bh = minix_find_entry(dir,name,len,&de);
  retval = -ENOENT;
  if (!bh)
    goto end_rmdir;
  retval = -EPERM;
  // 获取目录文件对应的inode
  if (!(inode = iget(dir->i_sb, de->inode)))
    goto end_rmdir;
    // 权限校验
        if ((dir->i_mode & S_ISVTX) && !fsuser() &&
            current->fsuid != inode->i_uid &&
            current->fsuid != dir->i_uid)
    goto end_rmdir;
  if (inode->i_dev != dir->i_dev)
    goto end_rmdir;
  if (inode == dir)  /* we may not delete ".", but "../dir" is ok */
    goto end_rmdir;
  if (!S_ISDIR(inode->i_mode)) {
    retval = -ENOTDIR;
    goto end_rmdir;
  }
  // 目录非空
  if (!empty_dir(inode)) {
    retval = -ENOTEMPTY;
    goto end_rmdir;
  }
  if (de->inode != inode->i_ino) {
    retval = -ENOENT;
    goto end_rmdir;
  }
  if (inode->i_count > 1) {
    retval = -EBUSY;
    goto end_rmdir;
  }
  if (inode->i_nlink != 2)
    printk("empty directory has nlink!=2 (%d)\n",inode->i_nlink);
  // 置为未使用
  de->inode = 0;
  dir->i_version = ++event;
  mark_buffer_dirty(bh, 1);
  inode->i_nlink=0;
  inode->i_dirt=1;
  inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
  // 删除了..,所以父目录链接数减一
  dir->i_nlink--;
  dir->i_dirt=1;
  retval = 0;
end_rmdir:
  iput(dir);
  iput(inode);
  brelse(bh);
  return retval;
}
// 链接数减一
int minix_unlink(struct inode * dir, const char * name, int len)
{
  int retval;
  struct inode * inode;
  struct buffer_head * bh;
  struct minix_dir_entry * de;

repeat:
  retval = -ENOENT;
  inode = NULL;
  bh = minix_find_entry(dir,name,len,&de);
  if (!bh)
    goto end_unlink;
  if (!(inode = iget(dir->i_sb, de->inode)))
    goto end_unlink;
  retval = -EPERM;
  if (S_ISDIR(inode->i_mode))
    goto end_unlink;
  if (de->inode != inode->i_ino) {
    iput(inode);
    brelse(bh);
    current->counter = 0;
    schedule();
    goto repeat;
  }
  if ((dir->i_mode & S_ISVTX) && !fsuser() &&
      current->fsuid != inode->i_uid &&
      current->fsuid != dir->i_uid)
    goto end_unlink;
  if (de->inode != inode->i_ino) {
    retval = -ENOENT;
    goto end_unlink;
  }
  if (!inode->i_nlink) {
    printk("Deleting nonexistent file (%04x:%lu), %d\n",
      inode->i_dev,inode->i_ino,inode->i_nlink);
    inode->i_nlink=1;
  }
  de->inode = 0;
  dir->i_version = ++event;
  mark_buffer_dirty(bh, 1);
  dir->i_ctime = dir->i_mtime = CURRENT_TIME;
  dir->i_dirt = 1;
  inode->i_nlink--;
  inode->i_ctime = dir->i_ctime;
  inode->i_dirt = 1;
  retval = 0;
end_unlink:
  brelse(bh);
  iput(inode);
  iput(dir);
  return retval;
}
// 生成一个文件保存另一个文件路径,即软链
int minix_symlink(struct inode * dir, const char * name, int len, const char * symname)
{
  struct minix_dir_entry * de;
  struct inode * inode = NULL;
  struct buffer_head * bh = NULL, * name_block = NULL;
  int i;
  char c;

  if (!(inode = minix_new_inode(dir))) {
    iput(dir);
    return -ENOSPC;
  }
  inode->i_mode = S_IFLNK | 0777;
  inode->i_op = &minix_symlink_inode_operations;
  name_block = minix_bread(inode,0,1);
  if (!name_block) {
    iput(dir);
    inode->i_nlink--;
    inode->i_dirt = 1;
    iput(inode);
    return -ENOSPC;
  }
  i = 0;
// 软链文章内容
  while (i < 1023 && (c=*(symname++)))
    name_block->b_data[i++] = c;
  name_block->b_data[i] = 0;
  mark_buffer_dirty(name_block, 1);
  brelse(name_block);
  inode->i_size = i;
  inode->i_dirt = 1;
  // 新增一个目录项
  bh = minix_find_entry(dir,name,len,&de);
  if (bh) {
    inode->i_nlink--;
    inode->i_dirt = 1;
    iput(inode);
    brelse(bh);
    iput(dir);
    return -EEXIST;
  }
  i = minix_add_entry(dir, name, len, &bh, &de);
  if (i) {
    inode->i_nlink--;
    inode->i_dirt = 1;
    iput(inode);
    iput(dir);
    return i;
  }
  de->inode = inode->i_ino;
  mark_buffer_dirty(bh, 1);
  brelse(bh);
  iput(dir);
  iput(inode);
  return 0;
}
// 增加一个硬链接
int minix_link(struct inode * oldinode, struct inode * dir, const char * name, int len)
{
  int error;
  struct minix_dir_entry * de;
  struct buffer_head * bh;

  if (S_ISDIR(oldinode->i_mode)) {
    iput(oldinode);
    iput(dir);
    return -EPERM;
  }
  if (oldinode->i_nlink >= MINIX_LINK_MAX) {
    iput(oldinode);
    iput(dir);
    return -EMLINK;
  }
  // 去重
  bh = minix_find_entry(dir,name,len,&de);
  if (bh) {
    brelse(bh);
    iput(dir);
    iput(oldinode);
    return -EEXIST;
  }
  // 查找空闲目录项,de是新目录项地址
  error = minix_add_entry(dir, name, len, &bh, &de);
  if (error) {
    iput(dir);
    iput(oldinode);
    return error;
  }
  // 保存inode号
  de->inode = oldinode->i_ino;
  mark_buffer_dirty(bh, 1);
  brelse(bh);
  iput(dir);
  // inode链接数减一,因为多了一个目录项指向他
  oldinode->i_nlink++;
  oldinode->i_ctime = CURRENT_TIME;
  oldinode->i_dirt = 1;
  iput(oldinode);
  return 0;
}
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程杂技 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档