linux-dont-flush-writes-on-exec-20080326
[openafs.git] / src / afs / LINUX / osi_vnodeops.c
index 468a7e5..ded2fa7 100644 (file)
@@ -50,8 +50,6 @@ RCSID
 #endif
 
 extern struct vcache *afs_globalVp;
-
-
 static ssize_t
 afs_linux_read(struct file *fp, char *buf, size_t count, loff_t * offp)
 {
@@ -75,7 +73,11 @@ afs_linux_read(struct file *fp, char *buf, size_t count, loff_t * offp)
     else {
            osi_FlushPages(vcp, credp); /* ensure stale pages are gone */
            AFS_GUNLOCK();
+#ifdef DO_SYNC_READ
+           code = do_sync_read(fp, buf, count, offp);
+#else
            code = generic_file_read(fp, buf, count, offp);
+#endif
            AFS_GLOCK();
     }
 
@@ -97,7 +99,6 @@ static ssize_t
 afs_linux_write(struct file *fp, const char *buf, size_t count, loff_t * offp)
 {
     ssize_t code = 0;
-    int code2 = 0;
     struct vcache *vcp = VTOAFS(fp->f_dentry->d_inode);
     struct vrequest treq;
     cred_t *credp = crref();
@@ -121,7 +122,11 @@ afs_linux_write(struct file *fp, const char *buf, size_t count, loff_t * offp)
        code = -code;
     else {
            AFS_GUNLOCK();
+#ifdef DO_SYNC_READ
+           code = do_sync_write(fp, buf, count, offp);
+#else
            code = generic_file_write(fp, buf, count, offp);
+#endif
            AFS_GLOCK();
     }
 
@@ -190,7 +195,8 @@ afs_linux_readdir(struct file *fp, void *dirbuf, filldir_t filldir)
        code = -ENOENT;
        goto out;
     }
-    ObtainReadLock(&avc->lock);
+    ObtainSharedLock(&avc->lock, 810);
+    UpgradeSToWLock(&avc->lock, 811);
     ObtainReadLock(&tdc->lock);
     /*
      * Make sure that the data in the cache is current. There are two
@@ -202,19 +208,27 @@ afs_linux_readdir(struct file *fp, void *dirbuf, filldir_t filldir)
           && (tdc->dflags & DFFetching)
           && hsame(avc->m.DataVersion, tdc->f.versionNo)) {
        ReleaseReadLock(&tdc->lock);
-       ReleaseReadLock(&avc->lock);
+       ReleaseSharedLock(&avc->lock);
        afs_osi_Sleep(&tdc->validPos);
-       ObtainReadLock(&avc->lock);
+       ObtainSharedLock(&avc->lock, 812);
        ObtainReadLock(&tdc->lock);
     }
     if (!(avc->states & CStatd)
        || !hsame(avc->m.DataVersion, tdc->f.versionNo)) {
        ReleaseReadLock(&tdc->lock);
-       ReleaseReadLock(&avc->lock);
+       ReleaseSharedLock(&avc->lock);
        afs_PutDCache(tdc);
        goto tagain;
     }
 
+    /* Set the readdir-in-progress flag, and downgrade the lock
+     * to shared so others will be able to acquire a read lock.
+     */
+    avc->states |= CReadDir;
+    avc->dcreaddir = tdc;
+    avc->readdir_pid = MyPidxx;
+    ConvertWToSLock(&avc->lock);
+
     /* Fill in until we get an error or we're done. This implementation
      * takes an offset in units of blobs, rather than bytes.
      */
@@ -229,16 +243,16 @@ afs_linux_readdir(struct file *fp, void *dirbuf, filldir_t filldir)
        if (!de)
            break;
 
-       ino = (avc->fid.Fid.Volume << 16) + ntohl(de->fid.vnode);
-       ino &= 0x7fffffff;      /* Assumes 32 bit ino_t ..... */
+       ino = afs_calc_inum (avc->fid.Fid.Volume, ntohl(de->fid.vnode));
+
        if (de->name)
            len = strlen(de->name);
        else {
            printf("afs_linux_readdir: afs_dir_GetBlob failed, null name (inode %lx, dirpos %d)\n", 
                   (unsigned long)&tdc->f.inode, dirpos);
            DRelease((struct buffer *) de, 0);
+           ReleaseSharedLock(&avc->lock);
            afs_PutDCache(tdc);
-           ReleaseReadLock(&avc->lock);
            code = -ENOENT;
            goto out;
        }
@@ -275,7 +289,14 @@ afs_linux_readdir(struct file *fp, void *dirbuf, filldir_t filldir)
                /* clean up from afs_FindVCache */
                afs_PutVCache(tvc);
            }
+           /* 
+            * If this is NFS readdirplus, then the filler is going to
+            * call getattr on this inode, which will deadlock if we're
+            * holding the GLOCK.
+            */
+           AFS_GUNLOCK();
            code = (*filldir) (dirbuf, de->name, len, offset, ino, type);
+           AFS_GLOCK();
        }
 #else
        code = (*filldir) (dirbuf, de->name, len, offset, ino);
@@ -292,7 +313,11 @@ afs_linux_readdir(struct file *fp, void *dirbuf, filldir_t filldir)
 
     ReleaseReadLock(&tdc->lock);
     afs_PutDCache(tdc);
-    ReleaseReadLock(&avc->lock);
+    UpgradeSToWLock(&avc->lock, 813);
+    avc->states &= ~CReadDir;
+    avc->dcreaddir = 0;
+    avc->readdir_pid = 0;
+    ReleaseSharedLock(&avc->lock);
     code = 0;
 
 out:
@@ -463,6 +488,25 @@ afs_linux_lock(struct file *fp, int cmd, struct file_lock *flp)
     code = afs_lockctl(vcp, &flock, cmd, credp);
     AFS_GUNLOCK();
 
+#ifdef AFS_LINUX24_ENV
+    if ((code == 0 || flp->fl_type == F_UNLCK) && 
+        (cmd == F_SETLK || cmd == F_SETLKW)) {
+#ifdef POSIX_LOCK_FILE_WAIT_ARG
+       code = posix_lock_file(fp, flp, 0);
+#else
+       flp->fl_flags &=~ FL_SLEEP;
+       code = posix_lock_file(fp, flp);
+#endif 
+       if (code && flp->fl_type != F_UNLCK) {
+           struct AFS_FLOCK flock2;
+           flock2 = flock;
+           flock2.l_type = F_UNLCK;
+           AFS_GLOCK();
+           afs_lockctl(vcp, &flock2, F_SETLK, credp);
+           AFS_GUNLOCK();
+       }
+    }
+#endif
     /* Convert flock back to Linux's file_lock */
     flp->fl_type = flock.l_type;
     flp->fl_pid = flock.l_pid;
@@ -474,27 +518,88 @@ afs_linux_lock(struct file *fp, int cmd, struct file_lock *flp)
 
 }
 
+#ifdef STRUCT_FILE_OPERATIONS_HAS_FLOCK
+static int
+afs_linux_flock(struct file *fp, int cmd, struct file_lock *flp) {
+    int code = 0;
+    struct vcache *vcp = VTOAFS(FILE_INODE(fp));
+    cred_t *credp = crref();
+    struct AFS_FLOCK flock;
+    /* Convert to a lock format afs_lockctl understands. */
+    memset((char *)&flock, 0, sizeof(flock));
+    flock.l_type = flp->fl_type;
+    flock.l_pid = flp->fl_pid;
+    flock.l_whence = 0;
+    flock.l_start = 0;
+    flock.l_len = OFFSET_MAX;
+
+    /* Safe because there are no large files, yet */
+#if defined(F_GETLK64) && (F_GETLK != F_GETLK64)
+    if (cmd == F_GETLK64)
+       cmd = F_GETLK;
+    else if (cmd == F_SETLK64)
+       cmd = F_SETLK;
+    else if (cmd == F_SETLKW64)
+       cmd = F_SETLKW;
+#endif /* F_GETLK64 && F_GETLK != F_GETLK64 */
+
+    AFS_GLOCK();
+    code = afs_lockctl(vcp, &flock, cmd, credp);
+    AFS_GUNLOCK();
+
+    if ((code == 0 || flp->fl_type == F_UNLCK) && 
+        (cmd == F_SETLK || cmd == F_SETLKW)) {
+       flp->fl_flags &=~ FL_SLEEP;
+       code = flock_lock_file_wait(fp, flp);
+       if (code && flp->fl_type != F_UNLCK) {
+           struct AFS_FLOCK flock2;
+           flock2 = flock;
+           flock2.l_type = F_UNLCK;
+           AFS_GLOCK();
+           afs_lockctl(vcp, &flock2, F_SETLK, credp);
+           AFS_GUNLOCK();
+       }
+    }
+    /* Convert flock back to Linux's file_lock */
+    flp->fl_type = flock.l_type;
+    flp->fl_pid = flock.l_pid;
+
+    crfree(credp);
+    return -code;
+}
+#endif
+
 /* afs_linux_flush
  * essentially the same as afs_fsync() but we need to get the return
  * code for the sys_close() here, not afs_linux_release(), so call
  * afs_StoreAllSegments() with AFS_LASTSTORE
  */
 static int
+#if defined(FOP_FLUSH_TAKES_FL_OWNER_T)
+afs_linux_flush(struct file *fp, fl_owner_t id)
+#else
 afs_linux_flush(struct file *fp)
+#endif
 {
     struct vrequest treq;
-    struct vcache *vcp = VTOAFS(FILE_INODE(fp));
-    cred_t *credp = crref();
+    struct vcache *vcp;
+    cred_t *credp;
     int code;
 
     AFS_GLOCK();
 
+    if (fp->f_flags | O_RDONLY)     /* readers dont flush */
+       return 0;
+
+    credp = crref();
+    vcp = VTOAFS(FILE_INODE(fp));
+
     code = afs_InitReq(&treq, credp);
     if (code)
        goto out;
 
     ObtainSharedLock(&vcp->lock, 535);
-    if (vcp->execsOrWriters > 0) {
+    if ((vcp->execsOrWriters > 0) && (file_count(fp) == 1)) {
        UpgradeSToWLock(&vcp->lock, 536);
        code = afs_StoreAllSegments(vcp, &treq, AFS_SYNC | AFS_LASTSTORE);
        ConvertWToSLock(&vcp->lock);
@@ -544,6 +649,10 @@ struct file_operations afs_dir_fops = {
 struct file_operations afs_file_fops = {
   .read =      afs_linux_read,
   .write =     afs_linux_write,
+#ifdef GENERIC_FILE_AIO_READ
+  .aio_read =  generic_file_aio_read,
+  .aio_write = generic_file_aio_write,
+#endif
 #ifdef HAVE_UNLOCKED_IOCTL
   .unlocked_ioctl = afs_unlocked_xioctl,
 #else
@@ -555,12 +664,19 @@ struct file_operations afs_file_fops = {
   .mmap =      afs_linux_mmap,
   .open =      afs_linux_open,
   .flush =     afs_linux_flush,
-#ifdef AFS_LINUX26_ENV
+#if defined(AFS_LINUX26_ENV) && defined(STRUCT_FILE_OPERATIONS_HAS_SENDFILE)
   .sendfile =   generic_file_sendfile,
 #endif
+#if defined(AFS_LINUX26_ENV) && defined(STRUCT_FILE_OPERATIONS_HAS_SPLICE)
+  .splice_write = generic_file_splice_write,
+  .splice_read = generic_file_splice_read,
+#endif
   .release =   afs_linux_release,
   .fsync =     afs_linux_fsync,
   .lock =      afs_linux_lock,
+#ifdef STRUCT_FILE_OPERATIONS_HAS_FLOCK
+  .flock =     afs_linux_flock,
+#endif
 };
 
 
@@ -788,9 +904,17 @@ afs_dentry_iput(struct dentry *dp, struct inode *ip)
     struct vcache *vcp = VTOAFS(ip);
 
     AFS_GLOCK();
-    if (vcp->states & CUnlinked)
-       (void) afs_InactiveVCache(vcp, NULL);
+    (void) afs_InactiveVCache(vcp, NULL);
     AFS_GUNLOCK();
+#ifdef DCACHE_NFSFS_RENAMED
+#ifdef AFS_LINUX26_ENV
+    spin_lock(&dp->d_lock);
+#endif
+    dp->d_flags &= ~DCACHE_NFSFS_RENAMED;   
+#ifdef AFS_LINUX26_ENV
+    spin_unlock(&dp->d_lock);
+#endif
+#endif
 
     iput(ip);
 }
@@ -852,6 +976,7 @@ afs_linux_create(struct inode *dip, struct dentry *dp, int mode)
 
        afs_getattr(vcp, &vattr, credp);
        afs_fill_inode(ip, &vattr);
+       insert_inode_hash(ip);
        dp->d_op = &afs_dentry_operations;
        dp->d_time = hgetlo(VTOAFS(dip)->m.DataVersion);
        d_instantiate(dp, ip);
@@ -883,6 +1008,9 @@ afs_linux_lookup(struct inode *dip, struct dentry *dp)
     struct vcache *vcp = NULL;
     const char *comp = dp->d_name.name;
     struct inode *ip = NULL;
+#if defined(AFS_LINUX26_ENV)
+    struct dentry *newdp = NULL;
+#endif
     int code;
 
 #if defined(AFS_LINUX26_ENV)
@@ -897,6 +1025,8 @@ afs_linux_lookup(struct inode *dip, struct dentry *dp)
        ip = AFSTOV(vcp);
        afs_getattr(vcp, &vattr, credp);
        afs_fill_inode(ip, &vattr);
+       if (hlist_unhashed(&ip->i_hash))
+           insert_inode_hash(ip);
     }
     dp->d_op = &afs_dentry_operations;
     dp->d_time = hgetlo(VTOAFS(dip)->m.DataVersion);
@@ -906,8 +1036,14 @@ afs_linux_lookup(struct inode *dip, struct dentry *dp)
     if (ip && S_ISDIR(ip->i_mode)) {
        struct dentry *alias;
 
+        /* Try to invalidate an existing alias in favor of our new one */
        alias = d_find_alias(ip);
+#if defined(AFS_LINUX26_ENV)
+        /* But not if it's disconnected; then we want d_splice_alias below */
+       if (alias && !(alias->d_flags & DCACHE_DISCONNECTED)) {
+#else
        if (alias) {
+#endif
            if (d_invalidate(alias) == 0) {
                dput(alias);
            } else {
@@ -915,12 +1051,17 @@ afs_linux_lookup(struct inode *dip, struct dentry *dp)
 #if defined(AFS_LINUX26_ENV)
                unlock_kernel();
 #endif
+               crfree(credp);
                return alias;
            }
        }
     }
 #endif
+#if defined(AFS_LINUX26_ENV)
+    newdp = d_splice_alias(ip, dp);
+#else
     d_add(dp, ip);
+#endif
 
 #if defined(AFS_LINUX26_ENV)
     unlock_kernel();
@@ -931,8 +1072,13 @@ afs_linux_lookup(struct inode *dip, struct dentry *dp)
      * seeing that the dp->d_inode field is NULL.
      */
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,10)
+#if defined(AFS_LINUX26_ENV)
+    if (!code || code == ENOENT)
+       return newdp;
+#else
     if (code == ENOENT)
        return ERR_PTR(0);
+#endif
     else
        return ERR_PTR(-code);
 #else
@@ -1007,6 +1153,17 @@ afs_linux_unlink(struct inode *dip, struct dentry *dp)
             }
             tvc->uncred = credp;
            tvc->states |= CUnlinked;
+#ifdef DCACHE_NFSFS_RENAMED
+#ifdef AFS_LINUX26_ENV
+           spin_lock(&dp->d_lock);
+#endif
+           dp->d_flags |= DCACHE_NFSFS_RENAMED;   
+#ifdef AFS_LINUX26_ENV
+           spin_unlock(&dp->d_lock);
+#endif
+#endif
+       } else {
+           osi_FreeSmallSpace(__name); 
        }
        AFS_GUNLOCK();
 
@@ -1195,7 +1352,7 @@ afs_linux_ireadlink(struct inode *ip, char *target, int maxlen, uio_seg_t seg)
        return -code;
 }
 
-#if !defined(AFS_LINUX24_ENV)
+#if !defined(USABLE_KERNEL_PAGE_SYMLINK_CACHE)
 /* afs_linux_readlink 
  * Fill target (which is in user space) with contents of symlink.
  */
@@ -1215,6 +1372,36 @@ afs_linux_readlink(struct dentry *dp, char *target, int maxlen)
 /* afs_linux_follow_link
  * a file system dependent link following routine.
  */
+#if defined(AFS_LINUX24_ENV)
+static int afs_linux_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+    int code;
+    char *name;
+
+    name = osi_Alloc(PATH_MAX);
+    if (!name) {
+       return -EIO;
+    }
+
+    AFS_GLOCK();
+    code = afs_linux_ireadlink(dentry->d_inode, name, PATH_MAX - 1, AFS_UIOSYS);
+    AFS_GUNLOCK();
+
+    if (code < 0) {
+       goto out;
+    }
+
+    name[code] = '\0';
+    code = vfs_follow_link(nd, name);
+
+out:
+    osi_Free(name, PATH_MAX);
+
+    return code;
+}
+
+#else /* !defined(AFS_LINUX24_ENV) */
+
 static struct dentry *
 afs_linux_follow_link(struct dentry *dp, struct dentry *basep,
                      unsigned int follow)
@@ -1248,7 +1435,8 @@ afs_linux_follow_link(struct dentry *dp, struct dentry *basep,
     AFS_GUNLOCK();
     return res;
 }
-#endif
+#endif /* AFS_LINUX24_ENV */
+#endif /* USABLE_KERNEL_PAGE_SYMLINK_CACHE */
 
 /* afs_linux_readpage
  * all reads come through here. A strategy-like read call.
@@ -1281,7 +1469,7 @@ afs_linux_readpage(struct file *fp, struct page *pp)
     clear_bit(PG_error, &pp->flags);
 #endif
 
-    setup_uio(&tuio, &iovec, (char *)address, offset, PAGESIZE, UIO_READ,
+    setup_uio(&tuio, &iovec, (char *)address, offset, PAGE_SIZE, UIO_READ,
              AFS_UIOSYS);
 #ifdef AFS_LINUX24_ENV
     lock_kernel();
@@ -1299,7 +1487,7 @@ afs_linux_readpage(struct file *fp, struct page *pp)
 
     if (!code) {
        if (tuio.uio_resid)     /* zero remainder of page */
-           memset((void *)(address + (PAGESIZE - tuio.uio_resid)), 0,
+           memset((void *)(address + (PAGE_SIZE - tuio.uio_resid)), 0,
                   tuio.uio_resid);
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
        flush_dcache_page(pp);
@@ -1613,7 +1801,7 @@ static struct inode_operations afs_dir_iops = {
 /* We really need a separate symlink set of ops, since do_follow_link()
  * determines if it _is_ a link by checking if the follow_link op is set.
  */
-#if defined(AFS_LINUX24_ENV)
+#if defined(USABLE_KERNEL_PAGE_SYMLINK_CACHE)
 static int
 afs_symlink_filler(struct file *file, struct page *page)
 {
@@ -1648,10 +1836,10 @@ afs_symlink_filler(struct file *file, struct page *page)
 static struct address_space_operations afs_symlink_aops = {
   .readpage =  afs_symlink_filler
 };
-#endif
+#endif /* USABLE_KERNEL_PAGE_SYMLINK_CACHE */
 
 static struct inode_operations afs_symlink_iops = {
-#if defined(AFS_LINUX24_ENV)
+#if defined(USABLE_KERNEL_PAGE_SYMLINK_CACHE)
   .readlink =          page_readlink,
 #if defined(HAVE_KERNEL_PAGE_FOLLOW_LINK)
   .follow_link =       page_follow_link,
@@ -1659,13 +1847,17 @@ static struct inode_operations afs_symlink_iops = {
   .follow_link =       page_follow_link_light,
   .put_link =           page_put_link,
 #endif
-  .setattr =           afs_notify_change,
-#else
+#else /* !defined(USABLE_KERNEL_PAGE_SYMLINK_CACHE) */
   .readlink =          afs_linux_readlink,
   .follow_link =       afs_linux_follow_link,
+#if !defined(AFS_LINUX24_ENV)
   .permission =                afs_linux_permission,
   .revalidate =                afs_linux_revalidate,
 #endif
+#endif /* USABLE_KERNEL_PAGE_SYMLINK_CACHE */
+#if defined(AFS_LINUX24_ENV)
+  .setattr =           afs_notify_change,
+#endif
 };
 
 void
@@ -1691,11 +1883,10 @@ afs_fill_inode(struct inode *ip, struct vattr *vattr)
 
     } else if (S_ISLNK(ip->i_mode)) {
        ip->i_op = &afs_symlink_iops;
-#if defined(AFS_LINUX24_ENV)
+#if defined(USABLE_KERNEL_PAGE_SYMLINK_CACHE)
        ip->i_data.a_ops = &afs_symlink_aops;
        ip->i_mapping = &ip->i_data;
 #endif
     }
 
-    /* insert_inode_hash(ip);  -- this would make iget() work (if we used it) */
 }