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

#include <string.h>		// ַͷļҪһЩйַǶ뺯
#include <errno.h>		// ͷļϵͳиֳš(Linus minix )
#include <linux/sched.h>	// ȳͷļṹtask_structʼ0 ݣ
// һЩйúͻȡǶʽຯ䡣
#include <linux/kernel.h>	// ںͷļһЩں˳úԭζ塣
#include <asm/segment.h>	// βͷļйضμĴǶʽຯ

#include <fcntl.h>		// ļͷļļĲƳŵĶ塣
#include <sys/stat.h>		// ļ״̬ͷļļļϵͳ״̬ṹstat{}ͳ

extern int sys_close (int fd);	// رļϵͳá(fs/open.c, 192)

//// ļ()
// fd Ƶļarg ָļСֵ
// ļ롣
static int
dupfd (unsigned int fd, unsigned int arg)
{
// ļֵһļNR_OPEN߸þļṹڣ
// س벢˳
  if (fd >= NR_OPEN || !current->filp[fd])
    return -EBADF;
// ָ¾ֵarg ļس벢˳
  if (arg >= NR_OPEN)
    return -EINVAL;
// ڵǰ̵ļṹָѰŴڵarg ûʹõ
  while (arg < NR_OPEN)
    if (current->filp[arg])
      arg++;
    else
      break;
// ҵ¾ֵarg ļس벢˳
  if (arg >= NR_OPEN)
    return -EMFILE;
// ִʱرձ־λͼиλþλҲexec()ຯʱرոþ
  current->close_on_exec &= ~(1 << arg);
// ļṹָԭfd ָ룬ļü1
  (current->filp[arg] = current->filp[fd])->f_count++;
  return arg;			// µļ
}

//// ļϵͳú
// ָļoldfd¾ֵnewfdnewfd Ѿ򿪣ȹر֮
int
sys_dup2 (unsigned int oldfd, unsigned int newfd)
{
  sys_close (newfd);		// newfd Ѿ򿪣ȹر֮
  return dupfd (oldfd, newfd);	// Ʋ¾
}

//// ļϵͳú
// ָļoldfd¾ֵǵǰСδþ
int
sys_dup (unsigned int fildes)
{
  return dupfd (fildes, 0);
}

//// ļϵͳú
// fd ļcmd ǲ(μinclude/fcntl.h23-30 )
int
sys_fcntl (unsigned int fd, unsigned int cmd, unsigned long arg)
{
  struct file *filp;

// ļֵһļNR_OPEN߸þļṹָΪգ
// س벢˳
  if (fd >= NR_OPEN || !(filp = current->filp[fd]))
    return -EBADF;
// ݲͬcmd зֱ
  switch (cmd)
    {
    case F_DUPFD:		// ļ
      return dupfd (fd, arg);
    case F_GETFD:		// ȡļִʱرձ־
      return (current->close_on_exec >> fd) & 1;
    case F_SETFD:		// þִʱرձ־arg λ0 λãرա
      if (arg & 1)
	current->close_on_exec |= (1 << fd);
      else
	current->close_on_exec &= ~(1 << fd);
      return 0;
    case F_GETFL:		// ȡļ״̬־ͷģʽ
      return filp->f_flags;
    case F_SETFL:		// ļ״̬ͷģʽ(arg ӡ־)
      filp->f_flags &= ~(O_APPEND | O_NONBLOCK);
      filp->f_flags |= arg & (O_APPEND | O_NONBLOCK);
      return 0;
    case F_GETLK:
    case F_SETLK:
    case F_SETLKW:		// δʵ֡
      return -1;
    default:
      return -1;
    }
}
