void cm_QueueBKGRequest(cm_scache_t *scp, cm_bkgProc_t *procp, afs_uint32 p1, afs_uint32 p2, afs_uint32 p3, afs_uint32 p4, cm_user_t *userp, cm_req_t *reqp) { cm_bkgRequest_t *rp, *rpq; afs_uint32 daemonID; int duplicate = 0; rp = malloc(sizeof(*rp)); memset(rp, 0, sizeof(*rp)); cm_HoldSCache(scp); rp->scp = scp; cm_HoldUser(userp); rp->userp = userp; rp->procp = procp; rp->p1 = p1; rp->p2 = p2; rp->p3 = p3; rp->p4 = p4; rp->req = *reqp; /* Use separate queues for fetch and store operations */ daemonID = scp->fid.hash % (cm_nDaemons/2) * 2; if (procp == cm_BkgStore) daemonID++; lock_ObtainWrite(&cm_daemonLockp[daemonID]); /* Check to see if this is a duplicate request */ for (rpq = cm_bkgListpp[daemonID]; rpq; rpq = (cm_bkgRequest_t *) osi_QNext(&rpq->q)) { if ( rpq->p1 == p1 && rpq->p3 == p3 && rpq->procp == procp && rpq->p2 == p2 && rpq->p4 == p4 && rpq->scp == scp && rpq->userp == userp) { /* found a duplicate; update request with latest info */ duplicate = 1; break; } } if (!duplicate) { cm_bkgQueueCountp[daemonID]++; osi_QAddH((osi_queue_t **) &cm_bkgListpp[daemonID], (osi_queue_t **)&cm_bkgListEndpp[daemonID], &rp->q); } lock_ReleaseWrite(&cm_daemonLockp[daemonID]); if (duplicate) { cm_ReleaseSCache(scp); cm_ReleaseUser(userp); free(rp); } else { osi_Wakeup((LONG_PTR) &cm_bkgListpp[daemonID]); } }
/* called with locked scp; ensures that we have an ACL cache entry for the * user specified by the parameter "userp." * In pathological race conditions, this function may return success without * having loaded the entry properly (due to a racing callback revoke), so this * function must also be called in a while loop to make sure that we eventually * succeed. */ long cm_GetAccessRights(struct cm_scache *scp, struct cm_user *userp, struct cm_req *reqp) { long code; cm_fid_t tfid; cm_scache_t *aclScp = NULL; int got_cb = 0; cm_volume_t * volp = cm_FindVolumeByFID(&scp->fid, userp, reqp); /* pretty easy: just force a pass through the fetch status code */ osi_Log2(afsd_logp, "GetAccessRights scp 0x%p user 0x%p", scp, userp); /* first, start by finding out whether we have a directory or something * else, so we can find what object's ACL we need. */ if (scp->fileType == CM_SCACHETYPE_DIRECTORY || cm_accessPerFileCheck || !volp || (volp->flags & CM_VOLUMEFLAG_DFS_VOLUME)) { code = cm_SyncOp(scp, NULL, userp, reqp, 0, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_FORCECB); if (!code) cm_SyncOpDone(scp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS); else osi_Log3(afsd_logp, "GetAccessRights syncop failure scp %x user %x code %x", scp, userp, code); } else { /* not a dir, use parent dir's acl */ cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, scp->parentVnode, scp->parentUnique); lock_ReleaseWrite(&scp->rw); code = cm_GetSCache(&tfid, NULL, &aclScp, userp, reqp); if (code) { lock_ObtainWrite(&scp->rw); goto _done; } osi_Log2(afsd_logp, "GetAccessRights parent scp %x user %x", aclScp, userp); lock_ObtainWrite(&aclScp->rw); code = cm_SyncOp(aclScp, NULL, userp, reqp, 0, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS | CM_SCACHESYNC_FORCECB); if (!code) cm_SyncOpDone(aclScp, NULL, CM_SCACHESYNC_NEEDCALLBACK | CM_SCACHESYNC_GETSTATUS); else osi_Log3(afsd_logp, "GetAccessRights parent syncop failure scp %x user %x code %x", aclScp, userp, code); lock_ReleaseWrite(&aclScp->rw); cm_ReleaseSCache(aclScp); lock_ObtainWrite(&scp->rw); } _done: if (volp) cm_PutVolume(volp); return code; }
void cm_BkgDaemon(void * parm) { cm_bkgRequest_t *rp; afs_int32 code; char name[32] = ""; long daemonID = (long)parm; snprintf(name, sizeof(name), "cm_BkgDaemon_ShutdownEvent%d", daemonID); cm_BkgDaemon_ShutdownEvent[daemonID] = thrd_CreateEvent(NULL, FALSE, FALSE, name); if ( GetLastError() == ERROR_ALREADY_EXISTS ) afsi_log("Event Object Already Exists: %s", name); rx_StartClientThread(); lock_ObtainWrite(&cm_daemonLock); while (daemon_ShutdownFlag == 0) { if (powerStateSuspended) { Sleep(1000); continue; } if (!cm_bkgListEndp) { osi_SleepW((LONG_PTR)&cm_bkgListp, &cm_daemonLock); lock_ObtainWrite(&cm_daemonLock); continue; } /* we found a request */ for (rp = cm_bkgListEndp; rp; rp = (cm_bkgRequest_t *) osi_QPrev(&rp->q)) { if (cm_ServerAvailable(&rp->scp->fid, rp->userp) && !(rp->scp->flags & CM_SCACHEFLAG_DATASTORING)) break; } if (rp == NULL) { /* we couldn't find a request that we could process at the current time */ lock_ReleaseWrite(&cm_daemonLock); Sleep(1000); lock_ObtainWrite(&cm_daemonLock); continue; } osi_QRemoveHT((osi_queue_t **) &cm_bkgListp, (osi_queue_t **) &cm_bkgListEndp, &rp->q); osi_assertx(cm_bkgQueueCount-- > 0, "cm_bkgQueueCount 0"); lock_ReleaseWrite(&cm_daemonLock); osi_Log1(afsd_logp,"cm_BkgDaemon processing request 0x%p", rp); #ifdef DEBUG_REFCOUNT osi_Log2(afsd_logp,"cm_BkgDaemon (before) scp 0x%x ref %d",rp->scp, rp->scp->refCount); #endif code = (*rp->procp)(rp->scp, rp->p1, rp->p2, rp->p3, rp->p4, rp->userp); #ifdef DEBUG_REFCOUNT osi_Log2(afsd_logp,"cm_BkgDaemon (after) scp 0x%x ref %d",rp->scp, rp->scp->refCount); #endif /* * Keep the following list synchronized with the * error code list in cm_BkgStore. * cm_SyncOpDone(CM_SCACHESYNC_ASYNCSTORE) will be called there unless * one of these errors has occurred. */ switch ( code ) { case CM_ERROR_TIMEDOUT: /* or server restarting */ case CM_ERROR_RETRY: case CM_ERROR_WOULDBLOCK: case CM_ERROR_ALLBUSY: case CM_ERROR_ALLDOWN: case CM_ERROR_ALLOFFLINE: case CM_ERROR_PARTIALWRITE: if (rp->procp == cm_BkgStore) { osi_Log2(afsd_logp, "cm_BkgDaemon re-queueing failed request 0x%p code 0x%x", rp, code); lock_ObtainWrite(&cm_daemonLock); cm_bkgQueueCount++; osi_QAddT((osi_queue_t **) &cm_bkgListp, (osi_queue_t **)&cm_bkgListEndp, &rp->q); break; } /* otherwise fall through */ case 0: /* success */ default: /* other error */ if (code == 0) osi_Log1(afsd_logp,"cm_BkgDaemon SUCCESS: request 0x%p", rp); else osi_Log2(afsd_logp,"cm_BkgDaemon FAILED: request dropped 0x%p code 0x%x", rp, code); cm_ReleaseUser(rp->userp); cm_ReleaseSCache(rp->scp); free(rp); lock_ObtainWrite(&cm_daemonLock); } } lock_ReleaseWrite(&cm_daemonLock); thrd_SetEvent(cm_BkgDaemon_ShutdownEvent[daemonID]); }
/* called with scp write-locked, check to see if we have the ACL info we need * and can get it w/o blocking for any locks. * * Never drops the scp lock, but may fail if the access control info comes from * the parent directory, and the parent's scache entry can't be found, or it * can't be locked. Thus, this must always be called in a while loop to stabilize * things, since we can always lose the race condition getting to the parent vnode. */ int cm_HaveAccessRights(struct cm_scache *scp, struct cm_user *userp, afs_uint32 rights, afs_uint32 *outRightsp) { cm_scache_t *aclScp; long code; cm_fid_t tfid; int didLock; long trights; int release = 0; /* Used to avoid a call to cm_HoldSCache in the directory case */ didLock = 0; if (scp->fileType == CM_SCACHETYPE_DIRECTORY || cm_accessPerFileCheck) { aclScp = scp; /* not held, not released */ } else { cm_SetFid(&tfid, scp->fid.cell, scp->fid.volume, scp->parentVnode, scp->parentUnique); aclScp = cm_FindSCache(&tfid); if (!aclScp) return 0; if (aclScp != scp) { if (aclScp->fid.vnode < scp->fid.vnode) lock_ReleaseWrite(&scp->rw); lock_ObtainRead(&aclScp->rw); if (aclScp->fid.vnode < scp->fid.vnode) lock_ObtainWrite(&scp->rw); /* check that we have a callback, too */ if (!cm_HaveCallback(aclScp)) { /* can't use it */ lock_ReleaseRead(&aclScp->rw); cm_ReleaseSCache(aclScp); return 0; } didLock = 1; } release = 1; } lock_AssertAny(&aclScp->rw); /* now if rights is a subset of the public rights, we're done. * Otherwise, if we an explicit acl entry, we're also in good shape, * and can definitively answer. */ #ifdef AFS_FREELANCE_CLIENT if (cm_freelanceEnabled && aclScp->fid.cell==AFS_FAKE_ROOT_CELL_ID && aclScp->fid.volume==AFS_FAKE_ROOT_VOL_ID) { *outRightsp = aclScp->anyAccess; } else #endif if ((~aclScp->anyAccess & rights) == 0) { *outRightsp = rights; } else { /* we have to check the specific rights info */ code = cm_FindACLCache(aclScp, userp, &trights); if (code) { code = 0; goto done; } *outRightsp = trights; } if (scp->fileType > 0 && scp->fileType != CM_SCACHETYPE_DIRECTORY) { /* check mode bits */ if ((scp->unixModeBits & 0400) == 0) { osi_Log2(afsd_logp,"cm_HaveAccessRights UnixMode removing READ scp 0x%p unix 0%o", scp, scp->unixModeBits); *outRightsp &= ~PRSFS_READ; } if ((scp->unixModeBits & 0200) == 0 && (rights != (PRSFS_WRITE | PRSFS_LOCK))) { osi_Log2(afsd_logp,"cm_HaveAccessRights UnixMode removing WRITE scp 0x%p unix 0%o", scp, scp->unixModeBits); *outRightsp &= ~PRSFS_WRITE; } if ((scp->unixModeBits & 0200) == 0 && !cm_deleteReadOnly) { osi_Log2(afsd_logp,"cm_HaveAccessRights UnixMode removing DELETE scp 0x%p unix 0%o", scp, scp->unixModeBits); *outRightsp &= ~PRSFS_DELETE; } } /* if the user can insert, we must assume they can read/write as well * because we do not have the ability to determine if the current user * is the owner of the file. We will have to make the call to the * file server and let the file server tell us if the request should * be denied. */ if ((*outRightsp & PRSFS_INSERT) && (scp->creator == userp)) *outRightsp |= PRSFS_READ | PRSFS_WRITE; /* if the user can obtain a write-lock, read-locks are implied */ if (*outRightsp & PRSFS_WRITE) *outRightsp |= PRSFS_LOCK; code = 1; /* fall through */ done: if (didLock) lock_ReleaseRead(&aclScp->rw); if (release) cm_ReleaseSCache(aclScp); return code; }
void * cm_BkgDaemon(void * vparm) { cm_bkgRequest_t *rp; afs_int32 code; char name[32] = ""; long daemonID = (long)(LONG_PTR)vparm; snprintf(name, sizeof(name), "cm_BkgDaemon_ShutdownEvent%u", daemonID); cm_BkgDaemon_ShutdownEvent[daemonID] = thrd_CreateEvent(NULL, FALSE, FALSE, name); if ( GetLastError() == ERROR_ALREADY_EXISTS ) afsi_log("Event Object Already Exists: %s", name); rx_StartClientThread(); lock_ObtainWrite(&cm_daemonLockp[daemonID]); while (daemon_ShutdownFlag == 0) { int willBlock = 0; if (powerStateSuspended) { Sleep(1000); continue; } if (!cm_bkgListEndpp[daemonID]) { osi_SleepW((LONG_PTR)&cm_bkgListpp[daemonID], &cm_daemonLockp[daemonID]); lock_ObtainWrite(&cm_daemonLockp[daemonID]); continue; } /* we found a request */ for (rp = cm_bkgListEndpp[daemonID]; rp; rp = (cm_bkgRequest_t *) osi_QPrev(&rp->q)) { if (rp->scp->flags & CM_SCACHEFLAG_DELETED) break; /* * If the request has active I/O such that this worker would * be forced to block, leave the request in the queue and move * on to one that might be available for servicing. */ if (cm_RequestWillBlock(rp)) { willBlock++; continue; } if (cm_ServerAvailable(&rp->scp->fid, rp->userp)) break; } if (rp == NULL) { /* * Couldn't find a request that we could process at the * current time. If there were requests that would cause * the worker to block, sleep for 25ms so it can promptly * respond when it is available. Otherwise, sleep for 1s. * * This polling cycle needs to be replaced with a proper * producer/consumer dynamic worker pool. */ osi_Log2(afsd_logp,"cm_BkgDaemon[%u] sleeping %dms all tasks would block", daemonID, willBlock ? 100 : 1000); lock_ReleaseWrite(&cm_daemonLockp[daemonID]); Sleep(willBlock ? 100 : 1000); lock_ObtainWrite(&cm_daemonLockp[daemonID]); continue; } osi_QRemoveHT((osi_queue_t **) &cm_bkgListpp[daemonID], (osi_queue_t **) &cm_bkgListEndpp[daemonID], &rp->q); osi_assertx(cm_bkgQueueCountp[daemonID]-- > 0, "cm_bkgQueueCount 0"); lock_ReleaseWrite(&cm_daemonLockp[daemonID]); osi_Log2(afsd_logp,"cm_BkgDaemon[%u] processing request 0x%p", daemonID, rp); if (rp->scp->flags & CM_SCACHEFLAG_DELETED) { osi_Log2(afsd_logp,"cm_BkgDaemon[%u] DELETED scp 0x%x", daemonID, rp->scp); code = CM_ERROR_BADFD; } else { #ifdef DEBUG_REFCOUNT osi_Log3(afsd_logp,"cm_BkgDaemon[%u] (before) scp 0x%x ref %d", daemonID, rp->scp, rp->scp->refCount); #endif code = (*rp->procp)(rp->scp, rp->p1, rp->p2, rp->p3, rp->p4, rp->userp, &rp->req); #ifdef DEBUG_REFCOUNT osi_Log3(afsd_logp,"cm_BkgDaemon[%u] (after) scp 0x%x ref %d", daemonID, rp->scp, rp->scp->refCount); #endif } /* * Keep the following list synchronized with the * error code list in cm_BkgStore. * cm_SyncOpDone(CM_SCACHESYNC_ASYNCSTORE) will be called there unless * one of these errors has occurred. */ switch ( code ) { case CM_ERROR_TIMEDOUT: /* or server restarting */ case CM_ERROR_RETRY: case CM_ERROR_WOULDBLOCK: case CM_ERROR_ALLBUSY: case CM_ERROR_ALLDOWN: case CM_ERROR_ALLOFFLINE: case CM_ERROR_PARTIALWRITE: if (rp->procp == cm_BkgStore || rp->procp == RDR_BkgFetch) { osi_Log3(afsd_logp, "cm_BkgDaemon[%u] re-queueing failed request 0x%p code 0x%x", daemonID, rp, code); lock_ObtainWrite(&cm_daemonLockp[daemonID]); cm_bkgQueueCountp[daemonID]++; osi_QAddT((osi_queue_t **) &cm_bkgListpp[daemonID], (osi_queue_t **)&cm_bkgListEndpp[daemonID], &rp->q); break; } /* otherwise fall through */ case 0: /* success */ default: /* other error */ if (code == 0) { osi_Log2(afsd_logp,"cm_BkgDaemon[%u] SUCCESS: request 0x%p", daemonID, rp); } else { osi_Log3(afsd_logp,"cm_BkgDaemon[%u] FAILED: request dropped 0x%p code 0x%x", daemonID, rp, code); } cm_ReleaseUser(rp->userp); cm_ReleaseSCache(rp->scp); free(rp); lock_ObtainWrite(&cm_daemonLockp[daemonID]); } } lock_ReleaseWrite(&cm_daemonLockp[daemonID]); thrd_SetEvent(cm_BkgDaemon_ShutdownEvent[daemonID]); pthread_exit(NULL); return NULL; }