root/fs/open.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sys_ustat
  2. sys_statfs
  3. sys_fstatfs
  4. sys_truncate
  5. sys_ftruncate
  6. sys_utime
  7. sys_access
  8. sys_chdir
  9. sys_fchdir
  10. sys_chroot
  11. sys_fchmod
  12. sys_chmod
  13. sys_fchown
  14. sys_chown
  15. do_open
  16. sys_open
  17. sys_creat
  18. close_fp
  19. sys_close
  20. sys_vhangup

   1 /*
   2  *  linux/fs/open.c
   3  *
   4  *  Copyright (C) 1991, 1992  Linus Torvalds
   5  */
   6 
   7 #include <linux/vfs.h>
   8 #include <linux/types.h>
   9 #include <linux/utime.h>
  10 #include <linux/errno.h>
  11 #include <linux/fcntl.h>
  12 #include <linux/stat.h>
  13 #include <linux/string.h>
  14 #include <linux/sched.h>
  15 #include <linux/kernel.h>
  16 #include <linux/signal.h>
  17 #include <linux/tty.h>
  18 #include <linux/time.h>
  19 
  20 #include <asm/segment.h>
  21 
  22 extern void fcntl_remove_locks(struct task_struct *, struct file *, unsigned int fd);
  23 
  24 asmlinkage int sys_ustat(int dev, struct ustat * ubuf)
     /* [previous][next][first][last][top][bottom][index][help] */
  25 {
  26         return -ENOSYS;
  27 }
  28 
  29 asmlinkage int sys_statfs(const char * path, struct statfs * buf)
     /* [previous][next][first][last][top][bottom][index][help] */
  30 {
  31         struct inode * inode;
  32         int error;
  33 
  34         error = verify_area(VERIFY_WRITE, buf, sizeof(struct statfs));
  35         if (error)
  36                 return error;
  37         error = namei(path,&inode);
  38         if (error)
  39                 return error;
  40         if (!inode->i_sb->s_op->statfs) {
  41                 iput(inode);
  42                 return -ENOSYS;
  43         }
  44         inode->i_sb->s_op->statfs(inode->i_sb, buf);
  45         iput(inode);
  46         return 0;
  47 }
  48 
  49 asmlinkage int sys_fstatfs(unsigned int fd, struct statfs * buf)
     /* [previous][next][first][last][top][bottom][index][help] */
  50 {
  51         struct inode * inode;
  52         struct file * file;
  53         int error;
  54 
  55         error = verify_area(VERIFY_WRITE, buf, sizeof(struct statfs));
  56         if (error)
  57                 return error;
  58         if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
  59                 return -EBADF;
  60         if (!(inode = file->f_inode))
  61                 return -ENOENT;
  62         if (!inode->i_sb->s_op->statfs)
  63                 return -ENOSYS;
  64         inode->i_sb->s_op->statfs(inode->i_sb, buf);
  65         return 0;
  66 }
  67 
  68 asmlinkage int sys_truncate(const char * path, unsigned int length)
     /* [previous][next][first][last][top][bottom][index][help] */
  69 {
  70         struct inode * inode;
  71         int error;
  72 
  73         error = namei(path,&inode);
  74         if (error)
  75                 return error;
  76         if (S_ISDIR(inode->i_mode) || !permission(inode,MAY_WRITE)) {
  77                 iput(inode);
  78                 return -EACCES;
  79         }
  80         if (IS_RDONLY(inode)) {
  81                 iput(inode);
  82                 return -EROFS;
  83         }
  84         if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) {
  85                 iput(inode);
  86                 return -EPERM;
  87         }
  88         inode->i_size = length;
  89         if (inode->i_op && inode->i_op->truncate)
  90                 inode->i_op->truncate(inode);
  91         inode->i_ctime = inode->i_mtime = CURRENT_TIME;
  92         inode->i_dirt = 1;
  93         error = notify_change(NOTIFY_SIZE, inode);
  94         iput(inode);
  95         return error;
  96 }
  97 
  98 asmlinkage int sys_ftruncate(unsigned int fd, unsigned int length)
     /* [previous][next][first][last][top][bottom][index][help] */
  99 {
 100         struct inode * inode;
 101         struct file * file;
 102 
 103         if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
 104                 return -EBADF;
 105         if (!(inode = file->f_inode))
 106                 return -ENOENT;
 107         if (S_ISDIR(inode->i_mode) || !(file->f_mode & 2))
 108                 return -EACCES;
 109         if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
 110                 return -EPERM;
 111         inode->i_size = length;
 112         if (inode->i_op && inode->i_op->truncate)
 113                 inode->i_op->truncate(inode);
 114         inode->i_ctime = inode->i_mtime = CURRENT_TIME;
 115         inode->i_dirt = 1;
 116         return notify_change(NOTIFY_SIZE, inode);
 117 }
 118 
 119 /* If times==NULL, set access and modification to current time,
 120  * must be owner or have write permission.
 121  * Else, update from *times, must be owner or super user.
 122  */
 123 asmlinkage int sys_utime(char * filename, struct utimbuf * times)
     /* [previous][next][first][last][top][bottom][index][help] */
 124 {
 125         struct inode * inode;
 126         long actime,modtime;
 127         int error;
 128 
 129         error = namei(filename,&inode);
 130         if (error)
 131                 return error;
 132         if (IS_RDONLY(inode)) {
 133                 iput(inode);
 134                 return -EROFS;
 135         }
 136         if (times) {
 137                 if ((current->fsuid != inode->i_uid) && !fsuser()) {
 138                         iput(inode);
 139                         return -EPERM;
 140                 }
 141                 actime = get_fs_long((unsigned long *) &times->actime);
 142                 modtime = get_fs_long((unsigned long *) &times->modtime);
 143                 inode->i_ctime = CURRENT_TIME;
 144         } else {
 145                 if ((current->fsuid != inode->i_uid) &&
 146                     !permission(inode,MAY_WRITE)) {
 147                         iput(inode);
 148                         return -EACCES;
 149                 }
 150                 actime = modtime = inode->i_ctime = CURRENT_TIME;
 151         }
 152         inode->i_atime = actime;
 153         inode->i_mtime = modtime;
 154         inode->i_dirt = 1;
 155         error = notify_change(NOTIFY_TIME, inode);
 156         iput(inode);
 157         return error;
 158 }
 159 
 160 /*
 161  * access() needs to use the real uid/gid, not the effective uid/gid.
 162  * We do this by temporarily setting fsuid/fsgid to the wanted values
 163  */
 164 asmlinkage int sys_access(const char * filename, int mode)
     /* [previous][next][first][last][top][bottom][index][help] */
 165 {
 166         struct inode * inode;
 167         int old_fsuid, old_fsgid;
 168         int res;
 169 
 170         if (mode != (mode & S_IRWXO))   /* where's F_OK, X_OK, W_OK, R_OK? */
 171                 return -EINVAL;
 172         old_fsuid = current->fsuid;
 173         old_fsgid = current->fsgid;
 174         current->fsuid = current->uid;
 175         current->fsgid = current->gid;
 176         res = namei(filename,&inode);
 177         if (!res) {
 178                 if (!permission(inode, mode))
 179                         res = -EACCES;
 180                 iput(inode);
 181         }
 182         current->fsuid = old_fsuid;
 183         current->fsgid = old_fsgid;
 184         return res;
 185 }
 186 
 187 asmlinkage int sys_chdir(const char * filename)
     /* [previous][next][first][last][top][bottom][index][help] */
 188 {
 189         struct inode * inode;
 190         int error;
 191 
 192         error = namei(filename,&inode);
 193         if (error)
 194                 return error;
 195         if (!S_ISDIR(inode->i_mode)) {
 196                 iput(inode);
 197                 return -ENOTDIR;
 198         }
 199         if (!permission(inode,MAY_EXEC)) {
 200                 iput(inode);
 201                 return -EACCES;
 202         }
 203         iput(current->fs->pwd);
 204         current->fs->pwd = inode;
 205         return (0);
 206 }
 207 
 208 asmlinkage int sys_fchdir(unsigned int fd)
     /* [previous][next][first][last][top][bottom][index][help] */
 209 {
 210         struct inode * inode;
 211         struct file * file;
 212 
 213         if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
 214                 return -EBADF;
 215         if (!(inode = file->f_inode))
 216                 return -ENOENT;
 217         if (!S_ISDIR(inode->i_mode))
 218                 return -ENOTDIR;
 219         if (!permission(inode,MAY_EXEC))
 220                 return -EACCES;
 221         iput(current->fs->pwd);
 222         current->fs->pwd = inode;
 223         inode->i_count++;
 224         return (0);
 225 }
 226 
 227 asmlinkage int sys_chroot(const char * filename)
     /* [previous][next][first][last][top][bottom][index][help] */
 228 {
 229         struct inode * inode;
 230         int error;
 231 
 232         error = namei(filename,&inode);
 233         if (error)
 234                 return error;
 235         if (!S_ISDIR(inode->i_mode)) {
 236                 iput(inode);
 237                 return -ENOTDIR;
 238         }
 239         if (!fsuser()) {
 240                 iput(inode);
 241                 return -EPERM;
 242         }
 243         iput(current->fs->root);
 244         current->fs->root = inode;
 245         return (0);
 246 }
 247 
 248 asmlinkage int sys_fchmod(unsigned int fd, mode_t mode)
     /* [previous][next][first][last][top][bottom][index][help] */
 249 {
 250         struct inode * inode;
 251         struct file * file;
 252 
 253         if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
 254                 return -EBADF;
 255         if (!(inode = file->f_inode))
 256                 return -ENOENT;
 257         if ((current->fsuid != inode->i_uid) && !fsuser())
 258                 return -EPERM;
 259         if (IS_RDONLY(inode))
 260                 return -EROFS;
 261         if (mode == (mode_t) -1)
 262                 mode = inode->i_mode;
 263         inode->i_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
 264         if (!fsuser() && !in_group_p(inode->i_gid))
 265                 inode->i_mode &= ~S_ISGID;
 266         inode->i_ctime = CURRENT_TIME;
 267         inode->i_dirt = 1;
 268         return notify_change(NOTIFY_MODE, inode);
 269 }
 270 
 271 asmlinkage int sys_chmod(const char * filename, mode_t mode)
     /* [previous][next][first][last][top][bottom][index][help] */
 272 {
 273         struct inode * inode;
 274         int error;
 275 
 276         error = namei(filename,&inode);
 277         if (error)
 278                 return error;
 279         if ((current->fsuid != inode->i_uid) && !fsuser()) {
 280                 iput(inode);
 281                 return -EPERM;
 282         }
 283         if (IS_RDONLY(inode)) {
 284                 iput(inode);
 285                 return -EROFS;
 286         }
 287         if (mode == (mode_t) -1)
 288                 mode = inode->i_mode;
 289         inode->i_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO);
 290         if (!fsuser() && !in_group_p(inode->i_gid))
 291                 inode->i_mode &= ~S_ISGID;
 292         inode->i_ctime = CURRENT_TIME;
 293         inode->i_dirt = 1;
 294         error = notify_change(NOTIFY_MODE, inode);
 295         iput(inode);
 296         return error;
 297 }
 298 
 299 asmlinkage int sys_fchown(unsigned int fd, uid_t user, gid_t group)
     /* [previous][next][first][last][top][bottom][index][help] */
 300 {
 301         struct inode * inode;
 302         struct file * file;
 303         uid_t old_user;
 304         gid_t old_group;
 305         int notify_flag = 0;
 306 
 307         if (fd >= NR_OPEN || !(file = current->files->fd[fd]))
 308                 return -EBADF;
 309         if (!(inode = file->f_inode))
 310                 return -ENOENT;
 311         if (IS_RDONLY(inode))
 312                 return -EROFS;
 313         old_user = inode->i_uid;
 314         old_group = inode->i_gid;
 315         if (user == (uid_t) -1)
 316                 user = inode->i_uid;
 317         if (group == (gid_t) -1)
 318                 group = inode->i_gid;
 319         if ((current->fsuid == inode->i_uid && user == inode->i_uid &&
 320              (in_group_p(group) || group == inode->i_gid)) ||
 321             fsuser()) {
 322                 inode->i_uid = user;
 323                 inode->i_gid = group;
 324                 /*
 325                  * If the owner has been changed, remove the setuid bit
 326                  */
 327                 if (old_user != inode->i_uid && inode->i_mode & S_ISUID) {
 328                         inode->i_mode &= ~S_ISUID;
 329                         notify_flag = NOTIFY_MODE;
 330                 }
 331                 /*
 332                  * If the group has been changed, remove the setgid bit
 333                  */
 334                 if (old_group != inode->i_gid && inode->i_mode & S_ISGID) {
 335                         inode->i_mode &= ~S_ISGID;
 336                         notify_flag = NOTIFY_MODE;
 337                 }
 338                 inode->i_ctime = CURRENT_TIME;
 339                 inode->i_dirt = 1;
 340                 return notify_change(notify_flag | NOTIFY_UIDGID, inode);
 341         }
 342         return -EPERM;
 343 }
 344 
 345 asmlinkage int sys_chown(const char * filename, uid_t user, gid_t group)
     /* [previous][next][first][last][top][bottom][index][help] */
 346 {
 347         struct inode * inode;
 348         int error;
 349         uid_t old_user;
 350         gid_t old_group;
 351         int notify_flag = 0;
 352 
 353         error = lnamei(filename,&inode);
 354         if (error)
 355                 return error;
 356         if (IS_RDONLY(inode)) {
 357                 iput(inode);
 358                 return -EROFS;
 359         }
 360         old_user = inode->i_uid;
 361         old_group = inode->i_uid;
 362         if (user == (uid_t) -1)
 363                 user = inode->i_uid;
 364         if (group == (gid_t) -1)
 365                 group = inode->i_gid;
 366         if ((current->fsuid == inode->i_uid && user == inode->i_uid &&
 367              (in_group_p(group) || group == inode->i_gid)) ||
 368             fsuser()) {
 369                 inode->i_uid = user;
 370                 inode->i_gid = group;
 371                 /*
 372                  * If the owner has been changed, remove the setuid bit
 373                  */
 374                 if (old_user != inode->i_uid && inode->i_mode & S_ISUID) {
 375                         inode->i_mode &= ~S_ISUID;
 376                         notify_flag = NOTIFY_MODE;
 377                 }
 378                 /*
 379                  * If the group has been changed, remove the setgid bit
 380                  */
 381                 if (old_group != inode->i_gid && inode->i_mode & S_ISGID) {
 382                         inode->i_mode &= ~S_ISGID;
 383                         notify_flag = NOTIFY_MODE;
 384                 }
 385                 inode->i_ctime = CURRENT_TIME;
 386                 inode->i_dirt = 1;
 387                 error = notify_change(notify_flag | NOTIFY_UIDGID, inode);
 388                 iput(inode);
 389                 return error;
 390         }
 391         iput(inode);
 392         return -EPERM;
 393 }
 394 
 395 /*
 396  * Note that while the flag value (low two bits) for sys_open means:
 397  *      00 - read-only
 398  *      01 - write-only
 399  *      10 - read-write
 400  *      11 - special
 401  * it is changed into
 402  *      00 - no permissions needed
 403  *      01 - read-permission
 404  *      10 - write-permission
 405  *      11 - read-write
 406  * for the internal routines (ie open_namei()/follow_link() etc). 00 is
 407  * used by symlinks.
 408  */
 409 int do_open(const char * filename,int flags,int mode)
     /* [previous][next][first][last][top][bottom][index][help] */
 410 {
 411         struct inode * inode;
 412         struct file * f;
 413         int flag,error,fd;
 414 
 415         for(fd=0 ; fd<NR_OPEN ; fd++)
 416                 if (!current->files->fd[fd])
 417                         break;
 418         if (fd>=NR_OPEN)
 419                 return -EMFILE;
 420         FD_CLR(fd,&current->files->close_on_exec);
 421         f = get_empty_filp();
 422         if (!f)
 423                 return -ENFILE;
 424         current->files->fd[fd] = f;
 425         f->f_flags = flag = flags;
 426         f->f_mode = (flag+1) & O_ACCMODE;
 427         if (f->f_mode)
 428                 flag++;
 429         if (flag & (O_TRUNC | O_CREAT))
 430                 flag |= 2;
 431         error = open_namei(filename,flag,mode,&inode,NULL);
 432         if (error) {
 433                 current->files->fd[fd]=NULL;
 434                 f->f_count--;
 435                 return error;
 436         }
 437 
 438         f->f_inode = inode;
 439         f->f_pos = 0;
 440         f->f_reada = 0;
 441         f->f_op = NULL;
 442         if (inode->i_op)
 443                 f->f_op = inode->i_op->default_file_ops;
 444         if (f->f_op && f->f_op->open) {
 445                 error = f->f_op->open(inode,f);
 446                 if (error) {
 447                         iput(inode);
 448                         f->f_count--;
 449                         current->files->fd[fd]=NULL;
 450                         return error;
 451                 }
 452         }
 453         f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
 454         return (fd);
 455 }
 456 
 457 asmlinkage int sys_open(const char * filename,int flags,int mode)
     /* [previous][next][first][last][top][bottom][index][help] */
 458 {
 459         char * tmp;
 460         int error;
 461 
 462         error = getname(filename, &tmp);
 463         if (error)
 464                 return error;
 465         error = do_open(tmp,flags,mode);
 466         putname(tmp);
 467         return error;
 468 }
 469 
 470 asmlinkage int sys_creat(const char * pathname, int mode)
     /* [previous][next][first][last][top][bottom][index][help] */
 471 {
 472         return sys_open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode);
 473 }
 474 
 475 int close_fp(struct file *filp, unsigned int fd)
     /* [previous][next][first][last][top][bottom][index][help] */
 476 {
 477         struct inode *inode;
 478 
 479         if (filp->f_count == 0) {
 480                 printk("VFS: Close: file count is 0\n");
 481                 return 0;
 482         }
 483         inode = filp->f_inode;
 484         if (inode)
 485                 fcntl_remove_locks(current, filp, fd);
 486         if (filp->f_count > 1) {
 487                 filp->f_count--;
 488                 return 0;
 489         }
 490         if (filp->f_op && filp->f_op->release)
 491                 filp->f_op->release(inode,filp);
 492         filp->f_count--;
 493         filp->f_inode = NULL;
 494         iput(inode);
 495         return 0;
 496 }
 497 
 498 asmlinkage int sys_close(unsigned int fd)
     /* [previous][next][first][last][top][bottom][index][help] */
 499 {       
 500         struct file * filp;
 501 
 502         if (fd >= NR_OPEN)
 503                 return -EBADF;
 504         FD_CLR(fd, &current->files->close_on_exec);
 505         if (!(filp = current->files->fd[fd]))
 506                 return -EBADF;
 507         current->files->fd[fd] = NULL;
 508         return (close_fp (filp, fd));
 509 }
 510 
 511 /*
 512  * This routine simulates a hangup on the tty, to arrange that users
 513  * are given clean terminals at login time.
 514  */
 515 asmlinkage int sys_vhangup(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 516 {
 517         if (!suser())
 518                 return -EPERM;
 519         /* If there is a controlling tty, hang it up */
 520         if (current->tty)
 521                 tty_vhangup(current->tty);
 522         return 0;
 523 }

/* [previous][next][first][last][top][bottom][index][help] */