root/fs/open.c

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

DEFINITIONS

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

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