macos fsevents hinting
authorDerrick Brashear <shadow@dementia.org>
Thu, 18 Mar 2010 19:27:35 +0000 (15:27 -0400)
committerDerrick Brashear <shadow@dementia.org>
Sat, 20 Mar 2010 01:46:05 +0000 (18:46 -0700)
add support for faking it. no exported interface exists, sadly.
currently does only authentication events, and is best-effort only,
however for people who get tokens after viewing directories in finder,
this is a drastic improvement.

also adds needed setting to afssettings plist

FIXES 23781

Change-Id: I46d5a6cf103c30a2134decccc929e1ef85a26726
Reviewed-on: http://gerrit.openafs.org/451
Reviewed-by: Derrick Brashear <shadow@dementia.org>
Tested-by: Derrick Brashear <shadow@dementia.org>

13 files changed:
src/afs/DARWIN/osi_machdep.h
src/afs/DARWIN/osi_misc.c
src/afs/DARWIN/osi_prototypes.h
src/afs/DARWIN/osi_vfsops.c
src/afs/DARWIN/osi_vnodeops.c
src/afs/VNOPS/afs_vnop_attrs.c
src/afs/afs.h
src/afs/afs_analyze.c
src/afs/afs_pioctl.c
src/afs/afs_prototypes.h
src/afs/afs_user.c
src/packaging/MacOS/OpenAFS.post_install
src/packaging/MacOS/settings.plist

index 6cd6a40..31b6763 100644 (file)
@@ -117,6 +117,8 @@ enum vcexcl { EXCL, NONEXCL };
 extern vfs_context_t afs_osi_ctxtp;
 extern int afs_osi_ctxtp_initialized;
 #endif
+extern u_int32_t afs_darwin_realmodes;
+extern u_int32_t afs_darwin_fsevents;
 
 /* 
  * Time related macros
index a90c24b..9ec11be 100644 (file)
 #endif
 
 #ifdef AFS_DARWIN80_ENV
+/* works like PFlushVolumeData */
+void
+darwin_notify_perms(struct unixuser *auser, int event)
+{
+    int i;
+    struct afs_q *tq, *uq = NULL;
+    struct vcache *tvc, *hnext;
+    int isglock = ISAFS_GLOCK();
+    struct vnode *vp;
+    struct vnode_attr va;
+
+    if (!afs_darwin_fsevents)
+       return;
+
+    VATTR_INIT(&va);
+    VATTR_SET(&va, va_mode, 0777);
+    if (event & UTokensObtained)
+       VATTR_SET(&va, va_uid, auser->uid);
+    else
+       VATTR_SET(&va, va_uid, -2); /* nobody */
+
+    get_vfs_context();
+    if (!isglock)
+       AFS_GLOCK();
+loop:
+    ObtainReadLock(&afs_xvcache);
+    for (i = 0; i < VCSIZE; i++) {
+       for (tq = afs_vhashTV[i].prev; tq != &afs_vhashTV[i]; tq = uq) {
+           uq = QPrev(tq);
+           tvc = QTOVH(tq);
+           if (tvc->f.states & CDeadVnode) {
+               /* we can afford to be best-effort */
+               continue;
+           }
+           /* no per-file acls, so only notify on directories */
+           if (!(vp = AFSTOV(tvc)) || !vnode_isdir(AFSTOV(tvc)))
+               continue;
+           /* dynroot object. no callbacks. anonymous ACL. just no. */
+           if (afs_IsDynrootFid(tvc))
+               continue;
+           /* no fake fsevents on mount point sources. leaks refs */
+           if (tvc->mvstat == 1)
+               continue;
+           /* if it's being reclaimed, just pass */
+           if (vnode_get(vp))
+               continue;
+           if (vnode_ref(vp)) {
+               AFS_GUNLOCK();
+               vnode_put(vp);
+               AFS_GLOCK();
+               continue;
+           }
+           ReleaseReadLock(&afs_xvcache);
+           ObtainWriteLock(&tvc->lock, 234);
+           tvc->f.states |= CEvent;
+           AFS_GUNLOCK();
+           vnode_setattr(vp, &va, afs_osi_ctxtp);
+           tvc->f.states &= ~CEvent;
+           vnode_put(vp);
+           AFS_GLOCK();
+           ReleaseWriteLock(&tvc->lock);
+           ObtainReadLock(&afs_xvcache);
+           /* our tvc ptr is still good until now */
+           AFS_FAST_RELE(tvc);
+       }
+    }
+    ReleaseReadLock(&afs_xvcache);
+    if (!isglock)
+       AFS_GUNLOCK();
+    put_vfs_context();
+}
+
 int
 osi_lookupname_user(user_addr_t aname, enum uio_seg seg, int followlink,
                    struct vnode **vpp) {
index 5cd4663..9898b34 100644 (file)
@@ -18,6 +18,7 @@
 extern afs_rwlock_t afs_xosi;
 
 /* osi_misc.c */
+extern void darwin_notify_perms(struct unixuser *auser, int event);
 extern int osi_lookupname(char *aname, enum uio_seg seg, int followlink,
                          struct vnode **vpp);
 extern int osi_lookupname_user(user_addr_t aname, enum uio_seg seg,
index 1754575..aa5af70 100644 (file)
@@ -447,17 +447,47 @@ afs_sync(struct mount *mp, int waitfor, struct ucred *cred, struct proc *p)
 }
 
 u_int32_t afs_darwin_realmodes = 0;
+u_int32_t afs_darwin_fsevents = 0;
+
+int
+afs_sysctl_int(int *name, u_int namelen, user_addr_t oldp, size_t *oldlenp,
+              user_addr_t newp, size_t newlen, u_int32_t *object)
+{
+#ifdef AFS_DARWIN80_ENV
+    int error;
+
+    if (oldp != USER_ADDR_NULL && oldlenp == NULL)
+       return (EFAULT);
+    if (oldp && *oldlenp < sizeof(u_int32_t))
+       return (ENOMEM);
+    if (newp && newlen != sizeof(u_int32_t))
+       return (EINVAL);
+    *oldlenp = sizeof(u_int32_t);
+    if (oldp) {
+       if ((error = copyout(object,
+                            oldp, sizeof(u_int32_t)))) {
+           return error;
+       }
+    }
+    if (newp)
+       return copyin(newp, object, sizeof(u_int32_t));
+    return 0;
+#else
+    return sysctl_int(oldp, oldlenp, newp, newlen,
+                     object);
+#endif
+}
 
 #ifdef AFS_DARWIN80_ENV
-int afs_sysctl(int *name, u_int namelen, user_addr_t oldp, size_t *oldlenp, 
+int
+afs_sysctl(int *name, u_int namelen, user_addr_t oldp, size_t *oldlenp,
               user_addr_t newp, size_t newlen, vfs_context_t context)
 #else
-int afs_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp, 
+int
+afs_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
               void *newp, size_t newlen, struct proc *p)
 #endif
 {
-    int error;
-
     switch (name[0]) {
     case AFS_SC_ALL:
         /* nothing defined */
@@ -469,28 +499,11 @@ int afs_sysctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
        case AFS_SC_DARWIN_ALL:
            switch (name[2]) {
            case AFS_SC_DARWIN_ALL_REALMODES:
-#ifdef AFS_DARWIN80_ENV
-               if (oldp != USER_ADDR_NULL && oldlenp == NULL)
-                   return (EFAULT);
-               if (oldp && *oldlenp < sizeof(u_int32_t))
-                   return (ENOMEM);
-               if (newp && newlen != sizeof(u_int32_t))
-                   return (EINVAL);
-               *oldlenp = sizeof(u_int32_t);
-               if (oldp) {
-                   if ((error = copyout(&afs_darwin_realmodes,
-                                        oldp, sizeof(u_int32_t)))) {
-                       return error;
-                   }
-               }
-               if (newp)
-                   return copyin(newp, &afs_darwin_realmodes,
-                                 sizeof(u_int32_t));
-               return 0;
-#else
-               return sysctl_int(oldp, oldlenp, newp, newlen,
-                                 &afs_darwin_realmodes);
-#endif
+               return afs_sysctl_int(name, namelen, oldp, oldlenp,
+                                     newp, newlen, &afs_darwin_realmodes);
+           case AFS_SC_DARWIN_ALL_FSEVENTS:
+               return afs_sysctl_int(name, namelen, oldp, oldlenp,
+                                     newp, newlen, &afs_darwin_fsevents);
            }
            break;
            /* darwin version specific sysctl's goes here */
index 9dadf2c..07d3939 100644 (file)
@@ -287,6 +287,13 @@ afs_vop_lookup(ap)
     struct proc *p;
 #ifdef AFS_DARWIN80_ENV
     vcp = VTOAFS(ap->a_dvp);
+    /*
+     * ._ file attribute mirroring touches this.
+     * we can't flag the vcache as there is none, so fail here.
+     * needed for fsevents support.
+     */
+    if (ap->a_context == afs_osi_ctxtp)
+       return ENOENT;
     if (vcp->mvstat != 1) {
        error = cache_lookup(ap->a_dvp, ap->a_vpp, ap->a_cnp);
        if (error == -1) 
@@ -515,6 +522,9 @@ afs_vop_close(ap)
     int code;
     struct vnode *vp = ap->a_vp;
     struct vcache *avc = VTOAFS(vp);
+    /* allows faking FSE_CONTENT_MODIFIED */
+    if (afs_osi_ctxtp == ap->a_context)
+       return 0;
     AFS_GLOCK();
     if (vop_cred)
        code = afs_close(avc, ap->a_fflag, vop_cred);
@@ -545,6 +555,14 @@ afs_vop_access(ap)
     struct vcache * tvc = VTOAFS(ap->a_vp);
     int bits=0;
     int cmb = CHECK_MODE_BITS;
+#ifdef AFS_DARWIN80_ENV
+    /*
+     * needed for fsevents. ._ file attribute mirroring touches this.
+     * we can't flag the vcache, as there is none, so fail here.
+     */
+    if (ap->a_context == afs_osi_ctxtp)
+       return ENOENT;
+#endif
     AFS_GLOCK();
     afs_InitFakeStat(&fakestate);
     if ((code = afs_InitReq(&treq, vop_cred)))
@@ -671,11 +689,54 @@ afs_vop_getattr(ap)
 {
     int code;
 
-    AFS_GLOCK();
-    code = afs_getattr(VTOAFS(ap->a_vp), ap->a_vap, vop_cred);
-    /* This is legit; it just forces the fstrace event to happen */
-    code = afs_CheckCode(code, NULL, 58);
-    AFS_GUNLOCK();
+#ifdef AFS_DARWIN80_ENV
+    /* CEvent excludes the fsevent. our context excludes the ._ */
+    if ((VTOAFS(ap->a_vp)->f.states & CEvent) ||
+       (ap->a_context == afs_osi_ctxtp)){
+       struct vcache *avc = VTOAFS(ap->a_vp);
+       int isglock = ISAFS_GLOCK();
+
+       /* this is needed because of how and when we re-enter */
+       if (!isglock)
+         AFS_GLOCK();
+       /* do minimal work to return fake result for fsevents */
+       if (afs_fakestat_enable && VTOAFS(ap->a_vp)->mvstat == 1) {
+           struct afs_fakestat_state fakestat;
+           struct vrequest treq;
+
+           code = afs_InitReq(&treq, vop_cred);
+           if (code) {
+               if (!isglock)
+                   AFS_GUNLOCK();
+               return code;
+           }
+           afs_InitFakeStat(&fakestat);
+           /* expects GLOCK */
+           code = afs_TryEvalFakeStat(&avc, &fakestat, &treq);
+           if (code) {
+               if (!isglock)
+                   AFS_GUNLOCK();
+               afs_PutFakeStat(&fakestat);
+               return code;
+           }
+       }
+       code = afs_CopyOutAttrs(avc, ap->a_vap);
+       if (!isglock)
+           AFS_GUNLOCK();
+       if (0 && !code) {
+           /* tweak things so finder will recheck */
+           (ap->a_vap)->va_gid = ((ap->a_vap)->va_gid == 1) ? 2 : 1;
+           (ap->a_vap)->va_mode &= ~(VSGID);
+       }
+    } else
+#endif
+    {
+       AFS_GLOCK();
+       code = afs_getattr(VTOAFS(ap->a_vp), ap->a_vap, vop_cred);
+       /* This is legit; it just forces the fstrace event to happen */
+       code = afs_CheckCode(code, NULL, 58);
+       AFS_GUNLOCK();
+    }
 #ifdef AFS_DARWIN80_ENV
     VATTR_SET_SUPPORTED(ap->a_vap, va_type);
     VATTR_SET_SUPPORTED(ap->a_vap, va_mode);
@@ -706,6 +767,11 @@ afs_vop_setattr(ap)
                                 * } */ *ap;
 {
     int code;
+#ifdef AFS_DARWIN80_ENV
+    /* fsevents tries to set attributes. drop it. */
+    if (ap->a_context == afs_osi_ctxtp)
+       return 0;
+#endif
     AFS_GLOCK();
     code = afs_setattr(VTOAFS(ap->a_vp), ap->a_vap, vop_cred);
     /* This is legit; it just forces the fstrace event to happen */
index 980611a..cf7bfa1 100644 (file)
@@ -65,7 +65,6 @@ afs_CopyOutAttrs(register struct vcache *avc, register struct vattr *attrs)
     }
 #if defined(AFS_DARWIN_ENV)
     {
-       extern u_int32_t afs_darwin_realmodes;
        if (!afs_darwin_realmodes) {
            /* Mac OS X uses the mode bits to determine whether a file or
             * directory is accessible, and believes them, even though under
index aba83d8..e142341 100644 (file)
@@ -316,6 +316,10 @@ struct cell_alias {
  */
 #define TMP_UPAGNotReferenced  128
 
+/* unixuser notify events */
+#define UTokensObtained 1
+#define UTokensDropped  2
+
 /* values for afs_gcpags */
 enum { AFS_GCPAGS_NOTCOMPILED = 0, AFS_GCPAGS_OK =
        1, AFS_GCPAGS_USERDISABLED, AFS_GCPAGS_EPROC0, AFS_GCPAGS_EPROCN,
@@ -584,8 +588,10 @@ struct SimpleLocks {
 #define CVFlushed      0x00080000
 #ifdef AFS_LINUX22_ENV
 #define CPageWrite      0x00200000      /* to detect vm deadlock - linux */
-#else
+#elif defined(AFS_SGI_ENV)
 #define CWritingUFS    0x00200000      /* to detect vm deadlock - used by sgi */
+#elif defined(AFS_DARWIN80_ENV)
+#define CEvent          0x00200000      /* to preclude deadlock when sending events */
 #endif
 #define CCreating      0x00400000      /* avoid needless store after open truncate */
 #define CPageHog       0x00800000      /* AIX - dumping large cores is a page hog. */
index 9dd5489..8e4f716 100644 (file)
@@ -533,6 +533,7 @@ afs_Analyze(register struct afs_conn *aconn, afs_int32 acode,
            } else if (acode == RXKADEXPIRED) {
                aconn->forceConnectFS = 0;      /* don't check until new tokens set */
                aconn->user->states |= UTokensBad;
+               afs_NotifyUser(tu, UTokensDropped);
                afs_warnuser
                    ("afs: Tokens for user of AFS id %d for cell %s have expired\n",
                     tu->vid, aconn->srvr->server->cell->cellName);
@@ -549,6 +550,7 @@ afs_Analyze(register struct afs_conn *aconn, afs_int32 acode,
                    areq->tokenError = 0;
                    aconn->forceConnectFS = 0;  /* don't check until new tokens set */
                    aconn->user->states |= UTokensBad;
+                   afs_NotifyUser(tu, UTokensDropped);
                    afs_warnuser
                        ("afs: Tokens for user of AFS id %d for cell %s are discarded (rxkad error=%d)\n",
                         tu->vid, aconn->srvr->server->cell->cellName, acode);
@@ -562,12 +564,14 @@ afs_Analyze(register struct afs_conn *aconn, afs_int32 acode,
            } else if (acode == RXKADEXPIRED) {
                aconn->forceConnectFS = 0;      /* don't check until new tokens set */
                aconn->user->states |= UTokensBad;
+               afs_NotifyUser(tu, UTokensDropped);
                afs_warnuser
                    ("afs: Tokens for user %d for cell %s have expired\n",
                     areq->uid, aconn->srvr->server->cell->cellName);
            } else {
                aconn->forceConnectFS = 0;      /* don't check until new tokens set */
                aconn->user->states |= UTokensBad;
+               afs_NotifyUser(tu, UTokensDropped);
                afs_warnuser
                    ("afs: Tokens for user %d for cell %s are discarded (rxkad error = %d)\n",
                     areq->uid, aconn->srvr->server->cell->cellName, acode);
index b0d690f..345f2ef 100644 (file)
@@ -1874,6 +1874,7 @@ DECL_PIOCTL(PSetTokens)
     afs_SetPrimary(tu, flag);
     tu->tokenTime = osi_Time();
     afs_ResetUserConns(tu);
+    afs_NotifyUser(tu, UTokensObtained);
     afs_PutUser(tu, WRITE_LOCK);
 
     return 0;
@@ -2267,6 +2268,7 @@ DECL_PIOCTL(PGetTokens)
     if (((tu->states & UHasTokens) == 0)
        || (tu->ct.EndTimestamp < osi_Time())) {
        tu->states |= (UTokensBad | UNeedsReset);
+       afs_NotifyUser(tu, UTokensDropped);
        afs_PutUser(tu, READ_LOCK);
        return ENOTCONN;
     }
@@ -2345,6 +2347,7 @@ DECL_PIOCTL(PUnlog)
            memset(&tu->ct, 0, sizeof(struct ClearToken));
            tu->refCount++;
            ReleaseWriteLock(&afs_xuser);
+           afs_NotifyUser(tu, UTokensDropped);
            /* We have to drop the lock over the call to afs_ResetUserConns,
             * since it obtains the afs_xvcache lock.  We could also keep
             * the lock, and modify ResetUserConns to take parm saying we
index 8db9f00..2fc653c 100644 (file)
@@ -921,6 +921,8 @@ extern struct unixuser *afs_FindUser(afs_int32 auid, afs_int32 acell,
                                     afs_int32 locktype);
 extern struct unixuser *afs_GetUser(register afs_int32 auid, afs_int32 acell,
                                    afs_int32 locktype);
+extern void afs_NotifyUser(struct unixuser *auser, int event);
+
 #if AFS_GCPAGS
 extern afs_int32 afs_GCPAGs(afs_int32 * ReleasedCount);
 extern void afs_GCPAGs_perproc_func(afs_proc_t * pproc);
index db9be5b..3e9fd95 100644 (file)
@@ -561,6 +561,13 @@ afs_SetPrimary(register struct unixuser *au, register int aflag)
 
 }                              /*afs_SetPrimary */
 
+void
+afs_NotifyUser(struct unixuser *auser, int event)
+{
+#ifdef AFS_DARWIN_ENV
+    darwin_notify_perms(auser, event);
+#endif
+}
 
 /**
  * Mark all of the unixuser records held for a particular PAG as
index 8f569d2..fcb1469 100644 (file)
@@ -116,6 +116,8 @@ if [ $majorvers -ge 7 ]; then
   # make config/settings.plist if it doesn't exist
   if [ ! -e config/settings.plist -a -e config/settings.plist.orig ]; then
     cp config/settings.plist.orig config/settings.plist
+  else
+    /usr/libexec/PlistBuddy -c "Add :Darwin:All:FSEvents bool" config/settings.plist  && /usr/libexec/PlistBuddy -c "Set :Darwin:All:FSEvents true" config/settings.plist
   fi
 elif [ -e config/afssettings ]; then
   # turn off execution of afssettings
index 36e8977..28f95d6 100644 (file)
@@ -8,6 +8,8 @@
                <dict>
                        <key>RealModes</key>
                        <false/>
+                       <key>FSEvents</key>
+                       <true/>
                </dict>
        </dict>
 </dict>