From e2134caed1812d523dc7c07a5307fd3ec53485f5 Mon Sep 17 00:00:00 2001 From: Derrick Brashear Date: Thu, 18 Mar 2010 15:27:35 -0400 Subject: [PATCH] macos fsevents hinting 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 Tested-by: Derrick Brashear --- src/afs/DARWIN/osi_machdep.h | 2 + src/afs/DARWIN/osi_misc.c | 72 ++++++++++++++++++++++ src/afs/DARWIN/osi_prototypes.h | 1 + src/afs/DARWIN/osi_vfsops.c | 65 ++++++++++++-------- src/afs/DARWIN/osi_vnodeops.c | 76 ++++++++++++++++++++++-- src/afs/VNOPS/afs_vnop_attrs.c | 1 - src/afs/afs.h | 8 ++- src/afs/afs_analyze.c | 4 ++ src/afs/afs_pioctl.c | 3 + src/afs/afs_prototypes.h | 2 + src/afs/afs_user.c | 7 +++ src/packaging/MacOS/OpenAFS.post_install | 2 + src/packaging/MacOS/settings.plist | 2 + 13 files changed, 212 insertions(+), 33 deletions(-) diff --git a/src/afs/DARWIN/osi_machdep.h b/src/afs/DARWIN/osi_machdep.h index 6cd6a40d4..31b676348 100644 --- a/src/afs/DARWIN/osi_machdep.h +++ b/src/afs/DARWIN/osi_machdep.h @@ -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 diff --git a/src/afs/DARWIN/osi_misc.c b/src/afs/DARWIN/osi_misc.c index a90c24bb1..9ec11bec6 100644 --- a/src/afs/DARWIN/osi_misc.c +++ b/src/afs/DARWIN/osi_misc.c @@ -20,6 +20,78 @@ #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) { diff --git a/src/afs/DARWIN/osi_prototypes.h b/src/afs/DARWIN/osi_prototypes.h index 5cd4663b1..9898b3437 100644 --- a/src/afs/DARWIN/osi_prototypes.h +++ b/src/afs/DARWIN/osi_prototypes.h @@ -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, diff --git a/src/afs/DARWIN/osi_vfsops.c b/src/afs/DARWIN/osi_vfsops.c index 175457557..aa5af700d 100644 --- a/src/afs/DARWIN/osi_vfsops.c +++ b/src/afs/DARWIN/osi_vfsops.c @@ -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 */ diff --git a/src/afs/DARWIN/osi_vnodeops.c b/src/afs/DARWIN/osi_vnodeops.c index 9dadf2cbb..07d3939ea 100644 --- a/src/afs/DARWIN/osi_vnodeops.c +++ b/src/afs/DARWIN/osi_vnodeops.c @@ -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 */ diff --git a/src/afs/VNOPS/afs_vnop_attrs.c b/src/afs/VNOPS/afs_vnop_attrs.c index 980611af5..cf7bfa1a7 100644 --- a/src/afs/VNOPS/afs_vnop_attrs.c +++ b/src/afs/VNOPS/afs_vnop_attrs.c @@ -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 diff --git a/src/afs/afs.h b/src/afs/afs.h index aba83d82b..e142341e6 100644 --- a/src/afs/afs.h +++ b/src/afs/afs.h @@ -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. */ diff --git a/src/afs/afs_analyze.c b/src/afs/afs_analyze.c index 9dd548986..8e4f71643 100644 --- a/src/afs/afs_analyze.c +++ b/src/afs/afs_analyze.c @@ -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); diff --git a/src/afs/afs_pioctl.c b/src/afs/afs_pioctl.c index b0d690f87..345f2ef24 100644 --- a/src/afs/afs_pioctl.c +++ b/src/afs/afs_pioctl.c @@ -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 diff --git a/src/afs/afs_prototypes.h b/src/afs/afs_prototypes.h index 8db9f00f7..2fc653c83 100644 --- a/src/afs/afs_prototypes.h +++ b/src/afs/afs_prototypes.h @@ -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); diff --git a/src/afs/afs_user.c b/src/afs/afs_user.c index db9be5b15..3e9fd9545 100644 --- a/src/afs/afs_user.c +++ b/src/afs/afs_user.c @@ -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 diff --git a/src/packaging/MacOS/OpenAFS.post_install b/src/packaging/MacOS/OpenAFS.post_install index 8f569d2bc..fcb14696b 100644 --- a/src/packaging/MacOS/OpenAFS.post_install +++ b/src/packaging/MacOS/OpenAFS.post_install @@ -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 diff --git a/src/packaging/MacOS/settings.plist b/src/packaging/MacOS/settings.plist index 36e897728..28f95d6d3 100644 --- a/src/packaging/MacOS/settings.plist +++ b/src/packaging/MacOS/settings.plist @@ -8,6 +8,8 @@ RealModes + FSEvents + -- 2.39.5