/*
* linux/fs/namei.c
*
* (C) 1991 Linus Torvalds
*/

/*
* Some corrections by tytso.
*/
/*
* tytso һЩ
*/

#include <linux/sched.h>	// ȳͷļṹtask_structʼ0 ݣ
// һЩйúͻȡǶʽຯ䡣
#include <linux/kernel.h>	// ںͷļһЩں˳úԭζ塣
#include <asm/segment.h>	// βͷļйضμĴǶʽຯ

#include <string.h>		// ַͷļҪһЩйַǶ뺯
#include <fcntl.h>		// ļͷļļĲƳŵĶ塣
#include <errno.h>		// ͷļϵͳиֳš(Linus minix )
#include <const.h>		// ͷļĿǰi ڵi_mode ֶεĸ־λ
#include <sys/stat.h>		// ļ״̬ͷļļļϵͳ״̬ṹstat{}ͳ

// ģʽꡣx include/fcntl.h 7 пʼļʱ־
// x ֵӦֵֵʾrwx Ȩ: r, w, rw, wxrwxrwx(ֵ8 )
#define ACC_MODE(x) ( "\004\002\006\377"[(x)&O_ACCMODE])

/*
* comment out this line if you want names > NAME_LEN chars to be
* truncated. Else they will be disallowed.
*/
/*
* ļ>NAME_LEN ַصͽ涨ע͵
*/
/* #define NO_TRUNCATE */

#define MAY_EXEC 1		// ִ(ɽ)
#define MAY_WRITE 2		// д
#define MAY_READ 4		// ɶ

/*
* permission()
*
* is used to check for read/write/execute permissions on a file.
* I don't know if we should look at just the euid or both euid and
* uid, but that should be easily changed.
*/
/*
* permission()
* úڼһļĶ/д/ִȨޡҲ֪Ƿֻeuid
* Ҫeuid uid ߣ޸ġ
*/
//// ļȨޡ
// inode - ļӦi ڵ㣻mask - 롣
// أɷ1򷵻0
static int
permission (struct m_inode *inode, int mask)
{
  int mode = inode->i_mode;	// ļ

/* special case: not even root can read/write a deleted file */
/* ʹǳû(root)Ҳܶ/дһѱɾļ */
// i ڵжӦ豸i ڵ0򷵻ء
  if (inode->i_dev && !inode->i_nlinks)
    return 0;
// ̵Чûid(euid)i ڵûid ͬȡļûȨޡ
  else if (current->euid == inode->i_uid)
    mode >>= 6;
// ̵Чid(egid)i ڵid ͬȡûķȨޡ
  else if (current->egid == inode->i_gid)
    mode >>= 3;
// ȡĵķȨͬǳû򷵻1򷵻0
  if (((mode & mask & 0007) == mask) || suser ())
    return 1;
  return 0;
}

/*
* ok, we cannot use strncmp, as the name is not in our data space.
* Thus we'll have to use match. No big problem. Match also makes
* some sanity tests.
*
* NOTE! unlike strncmp, match returns 1 for success, 0 for failure.
*/
/*
* okǲʹstrncmp ַȽϺΪƲǵݿռ(ں˿ռ)
* ֻʹmatch()ⲻmatch()ͬҲһЩĲԡ
*
* ע⣡strncmp ͬmatch()ɹʱ1ʧʱ0
*/
//// ַָȽϺ
// len - Ƚϵַȣname - ļָ룻de - Ŀ¼ṹ
// أͬ1ͬ0
static int
match (int len, const char *name, struct dir_entry *de)
{
  register int same __asm__ ("ax");

// Ŀ¼ָգĿ¼i ڵ0ҪȽϵַȳļȣ򷵻0
  if (!de || !de->inode || len > NAME_LEN)
    return 0;
// ҪȽϵĳlen СNAME_LENĿ¼ļȳlen򷵻0
  if (len < NAME_LEN && de->name[len])
    return 0;
// Ƕ䣬ûݿռ(fs)ִַıȽϲ
// %0 - eax(ȽϽsame)%1 - eax(eax ֵ0)%2 - esi(ָ)%3 - edi(Ŀ¼ָ)
// %4 - ecx(Ƚϵֽڳֵlen)
  __asm__ ("cld\n\t"		// 巽λ
	   "fs ; repe ; cmpsb\n\t"	// ûռִѭȽ[esi++][edi++]
	   "setz %%al"		// ȽϽһ(z=0)al=1(same=eax)
: "=a" (same): "" (0), "S" ((long) name), "D" ((long) de->name), "c" (len):"cx", "di",
	   "si");
  return same;			// رȽϽ
}

/*
* 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.
*
* This also takes care of the few special cases due to '..'-traversal
* over a pseudo-root and a mount point.
*/
/*
* find_entry()
* ָĿ¼ѰһƥĿ¼һҵĿ¼ĸ
* ԼĿ¼(Ϊһ - res_dir)Ŀ¼i ڵ - 
* ҪĻԼ
*
* '..'Ŀ¼ڼҲԼֱ - ԽһαĿ¼
* װ㡣
*/
//// ָĿ¼ļĿ¼
// dir - ָĿ¼i ڵָ룻name - ļnamelen - ļȣ
// أٻָ룻res_dir - صĿ¼ṹָ룻
static struct buffer_head *
find_entry (struct m_inode **dir,
	    const char *name, int namelen, struct dir_entry **res_dir)
{
  int entries;
  int block, i;
  struct buffer_head *bh;
  struct dir_entry *de;
  struct super_block *sb;

// NO_TRUNCATEļȳ󳤶NAME_LEN򷵻ء
#ifdef NO_TRUNCATE
  if (namelen > NAME_LEN)
    return NULL;
//ûжNO_TRUNCATEļȳ󳤶NAME_LENض֮
#else
  if (namelen > NAME_LEN)
    namelen = NAME_LEN;
#endif
// 㱾Ŀ¼Ŀ¼entriesÿշĿ¼ṹָ롣
  entries = (*dir)->i_size / (sizeof (struct dir_entry));
  *res_dir = NULL;
// ļȵ0򷵻NULL˳
  if (!namelen)
    return NULL;
/* check for '..', as we might have to do some "magic" for it */
/* Ŀ¼'..'ΪҪر */
  if (namelen == 2 && get_fs_byte (name) == '.'
      && get_fs_byte (name + 1) == '.')
    {
/* '..' in a pseudo-root results in a faked '.' (just change namelen) */
/* αе'..'ͬһ'.'(ֻıֳ) */
// ǰ̵ĸڵָ뼴ָĿ¼ļ޸Ϊ'.'
      if ((*dir) == current->root)
	namelen = 1;
// Ŀ¼i ڵŵROOT_INO(1)Ļ˵ļϵͳڵ㡣ȡļϵͳĳ顣
      else if ((*dir)->i_num == ROOT_INO)
	{
/* '..' over a mount-point results in 'dir' being exchanged for the mounted
directory-inode. NOTE! We set mounted, so that we can iput the new dir */
/* һװϵ'..'Ŀ¼װļϵͳĿ¼i ڵ㡣
ע⣡mounted ־ܹȡĿ¼ */
	  sb = get_super ((*dir)->i_dev);
// װi ڵڣͷԭi ڵ㣬ȻԱװi ڵд
// *dir ָñװi ڵ㣻i ڵ1
	  if (sb->s_imount)
	    {
	      iput (*dir);
	      (*dir) = sb->s_imount;
	      (*dir)->i_count++;
	    }
	}
    }
// i ڵָĵһֱӴ̿Ϊ0򷵻NULL˳
  if (!(block = (*dir)->i_zone[0]))
    return NULL;
// ӽڵ豸ȡָĿ¼ݿ飬ɹ򷵻NULL˳
  if (!(bh = bread ((*dir)->i_dev, block)))
    return NULL;
// Ŀ¼ݿƥָļĿ¼de ָݿ飬ڲĿ¼Ŀ¼
// £ѭִ
  i = 0;
  de = (struct dir_entry *) bh->b_data;
  while (i < entries)
    {
// ǰĿ¼ݿѾ꣬ûҵƥĿ¼ͷŵǰĿ¼ݿ顣
      if ((char *) de >= BLOCK_SIZE + bh->b_data)
	{
	  brelse (bh);
	  bh = NULL;
// ڶһĿ¼ݿ顣ΪգֻҪûĿ¼еĿ¼ÿ飬
// һĿ¼ݿ顣ÿ鲻գde ָĿ¼ݿ飬
	  if (!(block = bmap (*dir, i / DIR_ENTRIES_PER_BLOCK)) ||
	      !(bh = bread ((*dir)->i_dev, block)))
	    {
	      i += DIR_ENTRIES_PER_BLOCK;
	      continue;
	    }
	  de = (struct dir_entry *) bh->b_data;
	}
// ҵƥĿ¼Ļ򷵻ظĿ¼ṹָ͸Ŀ¼ݿָ룬˳
      if (match (namelen, name, de))
	{
	  *res_dir = de;
	  return bh;
	}
// Ŀ¼ݿбȽһĿ¼
      de++;
      i++;
    }
// ָĿ¼еĿ¼껹ûҵӦĿ¼ͷĿ¼ݿ飬NULL
  brelse (bh);
  return NULL;
}

/*
* add_entry()
*
* adds a file entry to the specified directory, using the same
* semantics as find_entry(). It returns NULL if it failed.
*
* 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.
*/
/*
* add_entry()
* ʹfind_entry()ͬķָĿ¼һļĿ¼
* ʧ򷵻NULL
*
* ע⣡'de'(ָĿ¼ṹָ)i ڵ㲿ֱΪ0 - ʾ
* ڵøúĿ¼Ϣ֮䲻˯ߣΪ˯ô
* ()ܻѾʹ˸Ŀ¼
*/
//// ָĿ¼ļĿ¼
// dir - ָĿ¼i ڵ㣻name - ļnamelen - ļȣ
// أٻָ룻res_dir - صĿ¼ṹָ룻
static struct buffer_head *
add_entry (struct m_inode *dir,
	   const char *name, int namelen, struct dir_entry **res_dir)
{
  int block, i;
  struct buffer_head *bh;
  struct dir_entry *de;

  *res_dir = NULL;
// NO_TRUNCATEļȳ󳤶NAME_LEN򷵻ء
#ifdef NO_TRUNCATE
  if (namelen > NAME_LEN)
    return NULL;
//ûжNO_TRUNCATEļȳ󳤶NAME_LENض֮
#else
  if (namelen > NAME_LEN)
    namelen = NAME_LEN;
#endif
// ļȵ0򷵻NULL˳
  if (!namelen)
    return NULL;
// Ŀ¼i ڵָĵһֱӴ̿Ϊ0򷵻NULL ˳
  if (!(block = dir->i_zone[0]))
    return NULL;
// ȡô̿ʧܣ򷵻NULL ˳
  if (!(bh = bread (dir->i_dev, block)))
    return NULL;
// Ŀ¼ݿѭδʹõĿ¼Ŀ¼ṹָde ָٻݿ
// ʼҲһĿ¼
  i = 0;
  de = (struct dir_entry *) bh->b_data;
  while (1)
    {
// ǰбĿ¼Ѿǰݿ飬ͷŸݿ飬һ̿block
// ʧܣ򷵻NULL˳
      if ((char *) de >= BLOCK_SIZE + bh->b_data)
	{
	  brelse (bh);
	  bh = NULL;
	  block = create_block (dir, i / DIR_ENTRIES_PER_BLOCK);
	  if (!block)
	    return NULL;
// ȡ̿鷵صָΪգÿ
	  if (!(bh = bread (dir->i_dev, block)))
	    {
	      i += DIR_ENTRIES_PER_BLOCK;
	      continue;
	    }
// Ŀ¼ṹָde ־ÿĸٻݿ鿪ʼ
	  de = (struct dir_entry *) bh->b_data;
	}
// ǰĿ¼i*Ŀ¼ṹСѾ˸Ŀ¼ָĴСi_size˵õi
// Ŀ¼δʹãǿʹǶԸĿ¼(øĿ¼i ڵָΪ)
// ¸Ŀ¼ĳֵ(һĿ¼ĳȣĿ¼i ڵ޸ı־ٸ¸Ŀ¼ĸıʱ
// Ϊǰʱ䡣
      if (i * sizeof (struct dir_entry) >= dir->i_size)
	{
	  de->inode = 0;
	  dir->i_size = (i + 1) * sizeof (struct dir_entry);
	  dir->i_dirt = 1;
	  dir->i_ctime = CURRENT_TIME;
	}
// Ŀ¼i ڵΪգʾҵһδʹõĿ¼ǸĿ¼޸ʱΪǰʱ䡣
// ûļĿ¼ļֶΣӦĸٻ޸ı־ظĿ¼
// ָԼøٻָ룬˳
      if (!de->inode)
	{
	  dir->i_mtime = CURRENT_TIME;
	  for (i = 0; i < NAME_LEN; i++)
	    de->name[i] = (i < namelen) ? get_fs_byte (name + i) : 0;
	  bh->b_dirt = 1;
	  *res_dir = de;
	  return bh;
	}
// Ŀ¼ѾʹãһĿ¼
      de++;
      i++;
    }
// ִвҲLinus дδʱȸfind_entry()Ĵ룬޸ĵ?
  brelse (bh);
  return NULL;
}

/*
* get_dir()
*
* Getdir traverses the pathname until it hits the topmost directory.
* It returns NULL on failure.
*/
/*
* get_dir()
* úݸ·ֱﵽ˵Ŀ¼
* ʧ򷵻NULL
*/
//// Ѱָ·Ŀ¼
// pathname - ·
// أĿ¼i ڵָ롣ʧʱNULL
static struct m_inode *
get_dir (const char *pathname)
{
  char c;
  const char *thisname;
  struct m_inode *inode;
  struct buffer_head *bh;
  int namelen, inr, idev;
  struct dir_entry *de;

// û趨i ڵ㣬߸ý̸i ڵΪ0ϵͳ
  if (!current->root || !current->root->i_count)
    panic ("No root inode");
// ̵ĵǰĿ¼ָΪգ߸õǰĿ¼i ڵüΪ0Ҳϵͳ⣬
  if (!current->pwd || !current->pwd->i_count)
    panic ("No cwd inode");
// ûָ·ĵ1 ַ'/'˵·Ǿ·Ӹi ڵ㿪ʼ
  if ((c = get_fs_byte (pathname)) == '/')
    {
      inode = current->root;
      pathname++;
// һַַʾ·Ӧӽ̵ĵǰĿ¼ʼ
// ȡ̵ǰĿ¼i ڵ㡣
    }
  else if (c)
    inode = current->pwd;
// ʾ·ΪգNULL˳
  else
    return NULL;		/* empty name is bad *//* յ·Ǵ */
// ȡõi ڵü1
  inode->i_count++;
  while (1)
    {
// i ڵ㲻Ŀ¼ڵ㣬ûпɽķɣͷŸi ڵ㣬NULL˳
      thisname = pathname;
      if (!S_ISDIR (inode->i_mode) || !permission (inode, MAY_EXEC))
	{
	  iput (inode);
	  return NULL;
	}
// ·ʼֱַַǽβ(NULL)'/'ʱnamelen ǵǰ
// Ŀ¼ĳȡҲһĿ¼ûм'/'򲻻᷵ظĿ¼i ڵ㣡
// 磺/var/log/httpdֻlog/Ŀ¼i ڵ㡣
      for (namelen = 0; (c = get_fs_byte (pathname++)) && (c != '/');
	   namelen++)
/* nothing */ ;
// ַǽβNULLѾָĿ¼򷵻ظi ڵָ룬˳
      if (!c)
	return inode;
// òָĿ¼ļĿ¼ڵǰĿ¼ѰĿ¼ûҵͷ
// i ڵ㣬NULL˳
      if (!(bh = find_entry (&inode, thisname, namelen, &de)))
	{
	  iput (inode);
	  return NULL;
	}
// ȡĿ¼i ڵinr 豸idevͷŰĿ¼ĸٻ͸i ڵ㡣
      inr = de->inode;
      idev = inode->i_dev;
      brelse (bh);
      iput (inode);
// ȡڵinr i ڵϢʧܣ򷵻NULL˳ԸĿ¼i ڵв
      if (!(inode = iget (idev, inr)))
	return NULL;
    }
}

/*
* dir_namei()
*
* dir_namei() returns the inode of the directory of the
* specified name, and the name within that directory.
*/
/*
* dir_namei()
* dir_namei()ָĿ¼i ڵָ룬ԼĿ¼ơ
*/
// pathname - Ŀ¼·namelen - ·ȡ
// أָĿ¼Ŀ¼i ڵָĿ¼䳤ȡ
static struct m_inode *
dir_namei (const char *pathname, int *namelen, const char **name)
{
  char c;
  const char *basename;
  struct m_inode *dir;

// ȡָ·Ŀ¼i ڵ㣬򷵻NULL˳
  if (!(dir = get_dir (pathname)))
    return NULL;
// ·pathname ⣬鴦һ'/'ַ䳤ȣ
// Ŀ¼i ڵָ롣
  basename = pathname;
  while (c = get_fs_byte (pathname++))
    if (c == '/')
      basename = pathname;
  *namelen = pathname - basename - 1;
  *name = basename;
  return dir;
}

/*
* namei()
*
* is used by most simple commands to get the inode of a specified name.
* Open, link etc use their own routines, but this is enough for things
* like 'chmod' etc.
*/
/*
* namei()
* ú򵥵ȡָ·Ƶi ڵ㡣openlink ʹ
* ԼӦ޸ģʽ'chmod'ú㹻ˡ
*/
//// ȡָ·i ڵ㡣
// pathname - ·
// أӦi ڵ㡣
struct m_inode *
namei (const char *pathname)
{
  const char *basename;
  int inr, dev, namelen;
  struct m_inode *dir;
  struct buffer_head *bh;
  struct dir_entry *de;

// Ȳָ·Ŀ¼Ŀ¼i ڵ㣬ڣ򷵻NULL˳
  if (!(dir = dir_namei (pathname, &namelen, &basename)))
    return NULL;
// صֵĳ0ʾ·һĿ¼Ϊһ
  if (!namelen)			/* special case: '/usr/' etc */
    return dir;			/* Ӧ'/usr/' */
// ڷصĶĿ¼ѰָļĿ¼i ڵ㡣ΪҲһĿ¼û
// м'/'򲻻᷵ظĿ¼i ڵ㣡磺/var/log/httpdֻlog/Ŀ¼i ڵ㡣
// dir_namei()'/'һֵһļҪ
// ʹѰĿ¼i ڵ㺯find_entry()д
  bh = find_entry (&dir, basename, namelen, &de);
  if (!bh)
    {
      iput (dir);
      return NULL;
    }
// ȡĿ¼i ڵźĿ¼豸ţͷŰĿ¼ĸٻԼĿ¼i ڵ㡣
  inr = de->inode;
  dev = dir->i_dev;
  brelse (bh);
  iput (dir);
// ȡӦںŵi ڵ㣬޸䱻ʱΪǰʱ䣬޸ı־󷵻ظi ڵָ롣
  dir = iget (dev, inr);
  if (dir)
    {
      dir->i_atime = CURRENT_TIME;
      dir->i_dirt = 1;
    }
  return dir;
}

/*
* open_namei()
*
* namei for open - this is in fact almost the whole open-routine.
*/
/*
* open_namei()
* open()ʹõnamei  - ʵĴļ
*/
//// ļnamei 
// pathname - ļ·flag - ļ򿪱־mode - ļԣ
// أɹ0򷵻س룻res_inode - صĶӦļ·ĵi ڵָ롣
int
open_namei (const char *pathname, int flag, int mode,
	    struct m_inode **res_inode)
{
  const char *basename;
  int inr, dev, namelen;
  struct m_inode *dir, *inode;
  struct buffer_head *bh;
  struct dir_entry *de;

// ļģʽ־ֻ(0)ļ0 ־O_TRUNC ȴλˣΪֻд־
  if ((flag & O_TRUNC) && !(flag & O_ACCMODE))
    flag |= O_WRONLY;
// ʹý̵ļ룬εģʽеӦλͨļ־
  mode &= 0777 & ~current->umask;
  mode |= I_REGULAR;
// ·ѰҵӦi ڵ㣬Լļ䳤ȡ
  if (!(dir = dir_namei (pathname, &namelen, &basename)))
    return -ENOENT;
// ļΪ0('/usr/'·)ô򿪲Ǵ0
// ʾһĿ¼ֱӷظĿ¼i ڵ㣬˳
  if (!namelen)
    {				/* special case: '/usr/' etc */
      if (!(flag & (O_ACCMODE | O_CREAT | O_TRUNC)))
	{
	  *res_inode = dir;
	  return 0;
	}
// ͷŸi ڵ㣬س롣
      iput (dir);
      return -EISDIR;
    }
// dir ڵӦĿ¼ȡļӦĿ¼ṹde ͸Ŀ¼ڵĸٻ
  bh = find_entry (&dir, basename, namelen, &de);
// øٻָΪNULLʾûҵӦļĿ¼ֻǴļ
  if (!bh)
    {
// ǴļͷŸĿ¼i ڵ㣬س˳
      if (!(flag & O_CREAT))
	{
	  iput (dir);
	  return -ENOENT;
	}
// ûڸĿ¼ûдȨͷŸĿ¼i ڵ㣬س˳
      if (!permission (dir, MAY_WRITE))
	{
	  iput (dir);
	  return -EACCES;
	}
// Ŀ¼ڵӦ豸һi ڵ㣬ʧܣͷĿ¼i ڵ㣬ûпռ롣
      inode = new_inode (dir->i_dev);
      if (!inode)
	{
	  iput (dir);
	  return -ENOSPC;
	}
// ʹøi ڵ㣬гʼãýڵûidӦڵģʽ޸ı־
      inode->i_uid = current->euid;
      inode->i_mode = mode;
      inode->i_dirt = 1;
// ȻָĿ¼dir һĿ¼
      bh = add_entry (dir, basename, namelen, &de);
// صӦúĿ¼ĸٻָΪNULLʾĿ¼ʧܡǽ
// i ڵӼ1ͷŸi ڵĿ¼i ڵ㣬س룬˳
      if (!bh)
	{
	  inode->i_nlinks--;
	  iput (inode);
	  iput (dir);
	  return -ENOSPC;
	}
// ʼøĿ¼i ڵΪ뵽i ڵĺ룻øٻ޸ı־Ȼ
// ͷŸøٻͷĿ¼i ڵ㡣Ŀ¼i ڵָ룬˳
      de->inode = inode->i_num;
      bh->b_dirt = 1;
      brelse (bh);
      iput (dir);
      *res_inode = inode;
      return 0;
    }
// Ŀ¼ȡļӦĿ¼ṹɹ(Ҳbh ΪNULL)ȡĿ¼i ڵ
// ڵ豸ţͷŸøٻԼĿ¼i ڵ㡣
  inr = de->inode;
  dev = dir->i_dev;
  brelse (bh);
  iput (dir);
// ռʹñ־O_EXCL λ򷵻ļѴڳ룬˳
  if (flag & O_EXCL)
    return -EEXIST;
// ȡĿ¼Ӧi ڵĲʧܣ򷵻طʳ룬˳
  if (!(inode = iget (dev, inr)))
    return -EACCES;
// i ڵһĿ¼Ľڵ㲢ҷģʽֻֻдûзʵȨޣͷŸi
// ڵ㣬طȨ޳룬˳
  if ((S_ISDIR (inode->i_mode) && (flag & O_ACCMODE)) ||
      !permission (inode, ACC_MODE (flag)))
    {
      iput (inode);
      return -EPERM;
    }
// ¸i ڵķʱֶΪǰʱ䡣
  inode->i_atime = CURRENT_TIME;
// ˽0 ־򽫸i ڵļȽΪ0
  if (flag & O_TRUNC)
    truncate (inode);
// 󷵻ظĿ¼i ڵָ룬0ɹ
  *res_inode = inode;
  return 0;
}

//// ϵͳú - һļͨļڵ(node)
// Ϊfilenamemode dev ָļϵͳڵ(ͨļ豸ļܵ)
// filename - ·mode - ָʹԼڵͣdev - 豸š
// أɹ򷵻0򷵻س롣
int
sys_mknod (const char *filename, int mode, int dev)
{
  const char *basename;
  int namelen;
  struct m_inode *dir, *inode;
  struct buffer_head *bh;
  struct dir_entry *de;

// ǳû򷵻طɳ롣
  if (!suser ())
    return -EPERM;
// ҲӦ·Ŀ¼i ڵ㣬򷵻س롣
  if (!(dir = dir_namei (filename, &namelen, &basename)))
    return -ENOENT;
// ˵ļΪ0˵·ûָļͷŸĿ¼i ڵ㣬
// 룬˳
  if (!namelen)
    {
      iput (dir);
      return -ENOENT;
    }
// ڸĿ¼ûдȨޣͷŸĿ¼i ڵ㣬طɳ룬˳
  if (!permission (dir, MAY_WRITE))
    {
      iput (dir);
      return -EPERM;
    }
// Ӧ·ļĿ¼ѾڣͷŰĿ¼ĸٻͷĿ¼
// i ڵ㣬ļѾڳ룬˳
  bh = find_entry (&dir, basename, namelen, &de);
  if (bh)
    {
      brelse (bh);
      iput (dir);
      return -EEXIST;
    }
// һµi ڵ㣬ɹͷĿ¼i ڵ㣬޿ռ룬˳
  inode = new_inode (dir->i_dev);
  if (!inode)
    {
      iput (dir);
      return -ENOSPC;
    }
// øi ڵģʽҪǿ豸ļַ豸ļi ڵֱӿ
// ָ0 豸š
  inode->i_mode = mode;
  if (S_ISBLK (mode) || S_ISCHR (mode))
    inode->i_zone[0] = dev;
// øi ڵ޸ʱ䡢ʱΪǰʱ䡣
  inode->i_mtime = inode->i_atime = CURRENT_TIME;
  inode->i_dirt = 1;
// Ŀ¼һĿ¼ʧ(Ŀ¼ĸٻָΪNULL)ͷĿ¼
// i ڵ㣻i ڵӼλͷŸi ڵ㡣س룬˳
  bh = add_entry (dir, basename, namelen, &de);
  if (!bh)
    {
      iput (dir);
      inode->i_nlinks = 0;
      iput (inode);
      return -ENOSPC;
    }
// Ŀ¼i ڵֶεi ڵţøٻ޸ı־ͷĿ¼µi ڵ㣬ͷ
// ٻ󷵻0(ɹ)
  de->inode = inode->i_num;
  bh->b_dirt = 1;
  iput (dir);
  iput (inode);
  brelse (bh);
  return 0;
}

//// ϵͳú - Ŀ¼
// pathname - ·mode - Ŀ¼ʹõȨԡ
// أɹ򷵻0򷵻س롣
int
sys_mkdir (const char *pathname, int mode)
{
  const char *basename;
  int namelen;
  struct m_inode *dir, *inode;
  struct buffer_head *bh, *dir_block;
  struct dir_entry *de;

// ǳû򷵻طɳ롣
  if (!suser ())
    return -EPERM;
// ҲӦ·Ŀ¼i ڵ㣬򷵻س롣
  if (!(dir = dir_namei (pathname, &namelen, &basename)))
    return -ENOENT;
// ˵ļΪ0˵·ûָļͷŸĿ¼i ڵ㣬
// 룬˳
  if (!namelen)
    {
      iput (dir);
      return -ENOENT;
    }
// ڸĿ¼ûдȨޣͷŸĿ¼i ڵ㣬طɳ룬˳
  if (!permission (dir, MAY_WRITE))
    {
      iput (dir);
      return -EPERM;
    }
// Ӧ·ļĿ¼ѾڣͷŰĿ¼ĸٻͷĿ¼
// i ڵ㣬ļѾڳ룬˳
  bh = find_entry (&dir, basename, namelen, &de);
  if (bh)
    {
      brelse (bh);
      iput (dir);
      return -EEXIST;
    }
// һµi ڵ㣬ɹͷĿ¼i ڵ㣬޿ռ룬˳
  inode = new_inode (dir->i_dev);
  if (!inode)
    {
      iput (dir);
      return -ENOSPC;
    }
// øi ڵӦļΪ32(һĿ¼ĴС)ýڵ޸ı־Լڵ޸ʱ
// ͷʱ䡣
  inode->i_size = 32;
  inode->i_dirt = 1;
  inode->i_mtime = inode->i_atime = CURRENT_TIME;
// Ϊi ڵһ̿飬ڵһֱӿָڸÿšʧܣͷŶӦĿ¼
// i ڵ㣻λi ڵӼͷŸµi ڵ㣬ûпռ룬˳
  if (!(inode->i_zone[0] = new_block (inode->i_dev)))
    {
      iput (dir);
      inode->i_nlinks--;
      iput (inode);
      return -ENOSPC;
    }
// øµi ڵ޸ı־
  inode->i_dirt = 1;
// Ĵ̿顣ͷŶӦĿ¼i ڵ㣻ͷĴ̿飻λi ڵ
// ӼͷŸµi ڵ㣬ûпռ룬˳
  if (!(dir_block = bread (inode->i_dev, inode->i_zone[0])))
    {
      iput (dir);
      free_block (inode->i_dev, inode->i_zone[0]);
      inode->i_nlinks--;
      iput (inode);
      return -ERROR;
    }
// de ָĿ¼ݿ飬øĿ¼i ڵֶεi ڵţֶε"."
  de = (struct dir_entry *) dir_block->b_data;
  de->inode = inode->i_num;
  strcpy (de->name, ".");
// Ȼde ָһĿ¼ṹýṹڴϼĿ¼Ľڵź".."
  de++;
  de->inode = dir->i_num;
  strcpy (de->name, "..");
  inode->i_nlinks = 2;
// Ȼøøٻ޸ı־ͷŸû
  dir_block->b_dirt = 1;
  brelse (dir_block);
// ʼi ڵģʽֶΣøi ڵ޸ı־
  inode->i_mode = I_DIRECTORY | (mode & 0777 & ~current->umask);
  inode->i_dirt = 1;
// Ŀ¼һĿ¼ʧ(Ŀ¼ĸٻָΪNULL)ͷĿ¼
// i ڵ㣻i ڵӼλͷŸi ڵ㡣س룬˳
  bh = add_entry (dir, basename, namelen, &de);
  if (!bh)
    {
      iput (dir);
      free_block (inode->i_dev, inode->i_zone[0]);
      inode->i_nlinks = 0;
      iput (inode);
      return -ENOSPC;
    }
// Ŀ¼i ڵֶεi ڵţøٻ޸ı־ͷĿ¼µi ڵ㣬ͷ
// ٻ󷵻0(ɹ)
  de->inode = inode->i_num;
  bh->b_dirt = 1;
  dir->i_nlinks++;
  dir->i_dirt = 1;
  iput (dir);
  iput (inode);
  brelse (bh);
  return 0;
}

/*
* routine to check that the specified directory is empty (for rmdir)
*/
/*
* ڼָĿ¼ǷΪյӳ(rmdir ϵͳú)
*/
//// ָĿ¼Ƿǿյġ
// inode - ָĿ¼i ڵָ롣
// أ0 - ǿյģ1 - ա
static int
empty_dir (struct m_inode *inode)
{
  int nr, block;
  int len;
  struct buffer_head *bh;
  struct dir_entry *de;

// ָĿ¼Ŀ¼ĸ(Ӧ2 "."".."ļĿ¼)
  len = inode->i_size / sizeof (struct dir_entry);
// Ŀ¼2 ߸Ŀ¼i ڵĵ1 ֱӿûָκδ̿ţӦ
// ʾϢ豸dev Ŀ¼0(ʧ)
  if (len < 2 || !inode->i_zone[0] ||
      !(bh = bread (inode->i_dev, inode->i_zone[0])))
    {
      printk ("warning - bad directory on dev %04x\n", inode->i_dev);
      return 0;
    }
// de ָж̿ݵĸٻе1 Ŀ¼
  de = (struct dir_entry *) bh->b_data;
// 1 Ŀ¼i ڵֵֶڸĿ¼i ڵţߵ2 Ŀ¼i ڵֶ
// Ϊ㣬Ŀ¼ֶβֱ"."".."ʾϢ豸dev Ŀ¼
// 0
  if (de[0].inode != inode->i_num || !de[1].inode ||
      strcmp (".", de[0].name) || strcmp ("..", de[1].name))
    {
      printk ("warning - bad directory on dev %04x\n", inode->i_dev);
      return 0;
    }
// nr Ŀ¼ţde ָĿ¼
  nr = 2;
  de += 2;
// ѭĿ¼еĿ¼(len-2 )ûĿ¼i ڵֶβΪ0(ʹ)
  while (nr < len)
    {
// ÿ̿еĿ¼Ѿ꣬ͷŸô̿ĸٻȡһ麬Ŀ¼
// ̿顣Ӧûʹ(ѾãļѾɾ)һ飬
// 0de ָ׸Ŀ¼
      if ((void *) de >= (void *) (bh->b_data + BLOCK_SIZE))
	{
	  brelse (bh);
	  block = bmap (inode, nr / DIR_ENTRIES_PER_BLOCK);
	  if (!block)
	    {
	      nr += DIR_ENTRIES_PER_BLOCK;
	      continue;
	    }
	  if (!(bh = bread (inode->i_dev, block)))
	    return 0;
	  de = (struct dir_entry *) bh->b_data;
	}
// Ŀ¼i ڵֶβ0ʾĿ¼ĿǰʹãͷŸøٻ
// 0˳
      if (de->inode)
	{
	  brelse (bh);
	  return 0;
	}
// ûвѯĿ¼еĿ¼⡣
      de++;
      nr++;
    }
// ˵Ŀ¼ûҵõĿ¼(Ȼͷ)򷵻ػ1
  brelse (bh);
  return 1;
}

//// ϵͳú - ɾָƵĿ¼
//  name - Ŀ¼(·)
// أ0 ʾɹ򷵻سš
int
sys_rmdir (const char *name)
{
  const char *basename;
  int namelen;
  struct m_inode *dir, *inode;
  struct buffer_head *bh;
  struct dir_entry *de;

// ǳû򷵻طɳ롣
  if (!suser ())
    return -EPERM;
// ҲӦ·Ŀ¼i ڵ㣬򷵻س롣
  if (!(dir = dir_namei (name, &namelen, &basename)))
    return -ENOENT;
// ˵ļΪ0˵·ûָļͷŸĿ¼i ڵ㣬
// 룬˳
  if (!namelen)
    {
      iput (dir);
      return -ENOENT;
    }
// ڸĿ¼ûдȨޣͷŸĿ¼i ڵ㣬طɳ룬˳
  if (!permission (dir, MAY_WRITE))
    {
      iput (dir);
      return -EPERM;
    }
// Ӧ·ļĿ¼ڣͷŰĿ¼ĸٻͷĿ¼
// i ڵ㣬ļѾڳ룬˳dir ǰҪɾĿ¼Ŀ¼i ڵ㣬de
// ҪɾĿ¼Ŀ¼ṹ
  bh = find_entry (&dir, basename, namelen, &de);
  if (!bh)
    {
      iput (dir);
      return -ENOENT;
    }
// ȡĿ¼ָi ڵ㡣ͷĿ¼i ڵ㣬ͷźĿ¼ĸٻ
// š
  if (!(inode = iget (dir->i_dev, de->inode)))
    {
      iput (dir);
      brelse (bh);
      return -EPERM;
    }
// Ŀ¼ɾ־ҽ̵Чûid ڸi ڵûidʾûȨɾ
// Ŀ¼ͷŰҪɾĿ¼Ŀ¼i ڵ͸ҪɾĿ¼i ڵ㣬ͷŸٻ
// س롣
  if ((dir->i_mode & S_ISVTX) && current->euid &&
      inode->i_uid != current->euid)
    {
      iput (dir);
      iput (inode);
      brelse (bh);
      return -EPERM;
    }
// ҪɾĿ¼i ڵ豸ŲڰĿ¼Ŀ¼豸ţ߸ñɾĿ¼
// Ӽ1(ʾзӵ)ɾĿ¼ͷŰҪɾĿ¼Ŀ¼
// i ڵ͸ҪɾĿ¼i ڵ㣬ͷŸٻس롣
  if (inode->i_dev != dir->i_dev || inode->i_count > 1)
    {
      iput (dir);
      iput (inode);
      brelse (bh);
      return -EPERM;
    }
// ҪɾĿ¼Ŀ¼i ڵĽڵŵڰɾĿ¼i ڵţʾͼɾ"."
// Ŀ¼ͷŰҪɾĿ¼Ŀ¼i ڵ͸ҪɾĿ¼i ڵ㣬ͷŸٻ
// 롣
  if (inode == dir)
    {				/* we may not delete ".", but "../dir" is ok */
      iput (inode);		/* ǲɾ"."ɾ"../dir" */
      iput (dir);
      brelse (bh);
      return -EPERM;
    }
// ҪɾĿ¼i ڵԱⲻһĿ¼ͷŰҪɾĿ¼Ŀ¼i ڵ
// ҪɾĿ¼i ڵ㣬ͷŸٻس롣
  if (!S_ISDIR (inode->i_mode))
    {
      iput (inode);
      iput (dir);
      brelse (bh);
      return -ENOTDIR;
    }
// 豻ɾĿ¼գͷŰҪɾĿ¼Ŀ¼i ڵ͸ҪɾĿ¼i ڵ㣬ͷ
// ٻس롣
  if (!empty_dir (inode))
    {
      iput (inode);
      iput (dir);
      brelse (bh);
      return -ENOTEMPTY;
    }
// 豻ɾĿ¼i ڵ2ʾϢ
  if (inode->i_nlinks != 2)
    printk ("empty directory has nlink!=2 (%d)", inode->i_nlinks);
// ø豻ɾĿ¼Ŀ¼i ڵֶΪ0ʾĿ¼ʹãúиĿ¼ĸ
// ޸ı־ͷŸû
  de->inode = 0;
  bh->b_dirt = 1;
  brelse (bh);
// ñɾĿ¼i ڵΪ0i ڵ޸ı־
  inode->i_nlinks = 0;
  inode->i_dirt = 1;
// ɾĿ¼Ŀ¼i ڵü1޸ıʱ޸ʱΪǰʱ䣬
// ýڵ޸ı־
  dir->i_nlinks--;
  dir->i_ctime = dir->i_mtime = CURRENT_TIME;
  dir->i_dirt = 1;
// ͷŰҪɾĿ¼Ŀ¼i ڵ͸ҪɾĿ¼i ڵ㣬0(ɹ)
  iput (dir);
  iput (inode);
  return 0;
}

//// ϵͳú - ɾļԼҲɾصļ
// ļϵͳɾһ֡һļһӣûн򿪸ļļ
// Ҳɾͷռõ豸ռ䡣
// name - ļ
// أɹ򷵻0򷵻سš
int
sys_unlink (const char *name)
{
  const char *basename;
  int namelen;
  struct m_inode *dir, *inode;
  struct buffer_head *bh;
  struct dir_entry *de;

// ҲӦ·Ŀ¼i ڵ㣬򷵻س롣
  if (!(dir = dir_namei (name, &namelen, &basename)))
    return -ENOENT;
// ˵ļΪ0˵·ûָļͷŸĿ¼i ڵ㣬
// 룬˳
  if (!namelen)
    {
      iput (dir);
      return -ENOENT;
    }
// ڸĿ¼ûдȨޣͷŸĿ¼i ڵ㣬طɳ룬˳
  if (!permission (dir, MAY_WRITE))
    {
      iput (dir);
      return -EPERM;
    }
// Ӧ·ļĿ¼ڣͷŰĿ¼ĸٻͷĿ¼
// i ڵ㣬ļѾڳ룬˳dir ǰҪɾĿ¼Ŀ¼i ڵ㣬de
// ҪɾĿ¼Ŀ¼ṹ
  bh = find_entry (&dir, basename, namelen, &de);
  if (!bh)
    {
      iput (dir);
      return -ENOENT;
    }
// ȡĿ¼ָi ڵ㡣ͷĿ¼i ڵ㣬ͷźĿ¼ĸٻ
// š
  if (!(inode = iget (dir->i_dev, de->inode)))
    {
      iput (dir);
      brelse (bh);
      return -ENOENT;
    }
// Ŀ¼ɾ־ûǳûҽ̵Чûid ڱɾļ
// i ڵûidҽ̵Чûid ҲĿ¼i ڵûidûȨɾļ
// ͷŸĿ¼i ڵ͸ļĿ¼i ڵ㣬ͷŰĿ¼Ļسš
  if ((dir->i_mode & S_ISVTX) && !suser () &&
      current->euid != inode->i_uid && current->euid != dir->i_uid)
    {
      iput (dir);
      iput (inode);
      brelse (bh);
      return -EPERM;
    }
// ָļһĿ¼ҲɾͷŸĿ¼i ڵ͸ļĿ¼i ڵ㣬ͷ
// Ŀ¼Ļسš
  if (S_ISDIR (inode->i_mode))
    {
      iput (inode);
      iput (dir);
      brelse (bh);
      return -EPERM;
    }
// i ڵѾΪ0ʾϢΪ1
  if (!inode->i_nlinks)
    {
      printk ("Deleting nonexistent file (%04x:%d), %d\n",
	      inode->i_dev, inode->i_num, inode->i_nlinks);
      inode->i_nlinks = 1;
    }
// ļĿ¼еi ڵֶΪ0ʾͷŸĿ¼ðĿ¼Ļ
// ޸ı־ͷŸøٻ
  de->inode = 0;
  bh->b_dirt = 1;
  brelse (bh);
// i ڵ1޸ı־¸ıʱΪǰʱ䡣ͷŸi ڵĿ¼i 
// 㣬0(ɹ)
  inode->i_nlinks--;
  inode->i_dirt = 1;
  inode->i_ctime = CURRENT_TIME;
  iput (inode);
  iput (dir);
  return 0;
}

//// ϵͳú - Ϊļһļ
// ΪһѾڵļһ(ҲΪӲ - hard link)
// oldname - ԭ·newname - µ·
// أɹ򷵻0򷵻سš
int
sys_link (const char *oldname, const char *newname)
{
  struct dir_entry *de;
  struct m_inode *oldinode, *dir;
  struct buffer_head *bh;
  const char *basename;
  int namelen;

// ȡԭļ·Ӧi ڵoldinodeΪ0ʾسš
  oldinode = namei (oldname);
  if (!oldinode)
    return -ENOENT;
// ԭ·ӦһĿ¼ͷŸi ڵ㣬سš
  if (S_ISDIR (oldinode->i_mode))
    {
      iput (oldinode);
      return -EPERM;
    }
// ·Ŀ¼i ڵ㣬ļ䳤ȡĿ¼i ڵûҵ
// ͷԭ·i ڵ㣬سš
  dir = dir_namei (newname, &namelen, &basename);
  if (!dir)
    {
      iput (oldinode);
      return -EACCES;
    }
// ·вļͷԭ·i ڵ·Ŀ¼i ڵ㣬سš
  if (!namelen)
    {
      iput (oldinode);
      iput (dir);
      return -EPERM;
    }
// ·Ŀ¼豸ԭ·豸ŲһҲܽӣͷ·
// Ŀ¼i ڵԭ·i ڵ㣬سš
  if (dir->i_dev != oldinode->i_dev)
    {
      iput (dir);
      iput (oldinode);
      return -EXDEV;
    }
// ûûĿ¼дȨޣҲܽӣͷ·Ŀ¼i ڵԭ·
// i ڵ㣬سš
  if (!permission (dir, MAY_WRITE))
    {
      iput (dir);
      iput (oldinode);
      return -EACCES;
    }
// ѯ·ǷѾڣڣҲܽӣͷŰѴĿ¼ĸ
// ͷ·Ŀ¼i ڵԭ·i ڵ㣬سš
  bh = find_entry (&dir, basename, namelen, &de);
  if (bh)
    {
      brelse (bh);
      iput (dir);
      iput (oldinode);
      return -EEXIST;
    }
// Ŀ¼һĿ¼ʧͷŸĿ¼i ڵԭ·i ڵ㣬سš
  bh = add_entry (dir, basename, namelen, &de);
  if (!bh)
    {
      iput (dir);
      iput (oldinode);
      return -ENOSPC;
    }
// ʼøĿ¼i ڵŵԭ·i ڵţðĿ¼ĸٻ
// ޸ı־ͷŸûͷĿ¼i ڵ㡣
  de->inode = oldinode->i_num;
  bh->b_dirt = 1;
  brelse (bh);
  iput (dir);
// ԭڵӦü1޸ıʱΪǰʱ䣬i ڵ޸ı־ͷԭ
// ·i ڵ㣬0(ɹ)
  oldinode->i_nlinks++;
  oldinode->i_ctime = CURRENT_TIME;
  oldinode->i_dirt = 1;
  iput (oldinode);
  return 0;
}
