def has_permission(self, access): # From draft23 9.1.2: # "For delegation stateids the access mode is based on the type of # delegation" if access == OPEN4_SHARE_ACCESS_WRITE and \ self.deleg_type == OPEN_DELEGATE_READ: raise NFS4Error(NFS4ERR_OPENMODE) # Is this the correct error???
def find_conflicts(self, key_template, range): """See if range conflicts with any lock not matching template Raises error containing info on a conlicting lock if any found. """ def match(template, key): for i,j in zip(template, key): if i==j or i is None: return True return False for e in self._tree.itervalues(): if match(key_template, e.key): # Ignore locks in subtree indicated by key continue for lock in e.locks: if range.conflicts(lock): if lock.expired: # STUB - expired always returns False pass # Set up the exception if lock.end == 0xffffffffffffffff: dlength = 0xffffffffffffffff else: dlength = lock.end + 1 - lock.start owner = lock_owner4(e.key[0].clientid, e.key[-1]) lock_denied = LOCK4denied(lock.start, dlength, lock.type, owner) raise NFS4Error(NFS4ERR_DENIED, lock_denied=lock_denied)
def close(self, key): # key = (client, open_owner) # client.config.allow_close_with_locks if self.types[BYTE].has_locks(key): if not client.config.allow_close_with_locks: raise NFS4Error(NFS4ERR_LOCKS_HELD) self.types[BYTE].remove_locks(key) self.types[SHARE].close(key) if self.has_no_state(): # BUG - ignores file locking if self.file.fattr4_numlinks == 0: self.file.destroy()
def grant_layout(self, state, layoutargs): # FIXME #if self.waiting > 0 or self.outstanding > 0: ## Don't grant layouts while anyone is waiting for a recall # return None if self.file.layout_options() == layoutargs.loga_layout_type: entry = self.grab_entry(state.key[:1], LayoutEntry) layout = self.file.get_layout(layoutargs) entry.populate(layout) log.debug("GRANTING layout: %s" % layout) return layout, entry raise NFS4Error(NFS4ERR_LAYOUTUNAVAILABLE)
def overlaps(self, start, end, exact=False): """Returns True if given range overlaps that of lock. If exact==True, an overlap that does not match the lock range exactly raises an error. """ out = start <= self.start <= end or \ self.start <= start <= self.end if out and exact: if self.start != start or self.end != end: raise NFS4Error(NFS4ERR_LOCK_RANGE) return out
def remove_lock(self, type, start, end): """Try to remove a lock for the lockowner implicit in self.key.""" old_lock = ByteLock(type, start, end) # Note type is ignored per draft22 18.12.3: # "Any legal value for locktype has no effect on the success or # failure of the LOCKU operation." if POSIXLOCK: self.remove_posix_lock(old_lock) else: try: self.locks.remove(old_lock) except ValueError: raise NFS4Error(NFS4ERR_LOCK_RANGE)
def test_share(self, access, deny=OPEN4_SHARE_DENY_NONE, error=NFS4ERR_SHARE_DENIED, client=None): """Check (access, deny) against all current shares. Raises error if there is a conflict. """ if access & 3 == 0: raise NFS4Error(NFS4ERR_INVAL) anon = 0 if self.anon0.read_count: anon |= OPEN4_SHARE_ACCESS_READ if self.anon0.write_count: anon |= OPEN4_SHARE_ACCESS_WRITE self.types[SHARE]._test_share(access, deny, error, anon)
def recall_conflicting_delegations(self, dispatcher, client, access, deny): # NOTE OK to have extra access/deny flags if not self.conflicts(client, access, deny): return # Recall everything for e in self._tree.itervalues(): if e.status == NORMAL: e.status = CB_INIT t = threading.Thread(target=e.initiate_recall, args=(dispatcher,)) t.setDaemon(True) t.start() # We need to release the lock so that delegations can be recalled, # which can involve operations like WRITE, LOCK, OPEN, etc, # that would otherwise block. The easiest way to do this is to # initiate shut down of this thread. raise NFS4Error(NFS4ERR_DELAY)
def _test_share(self, access, deny, error, anon): """Check (access, deny) against all current shares. Raises error if there is a conflict. """ # OK to use full access/deny if self.cache_valid: current_access, current_deny = self.cached_access, self.cached_deny else: # See draft22 9.7 current_access = current_deny = 0 for entry in self.itervalues(): current_access |= entry.share_access current_deny |= entry.share_deny self.cached_access, self.cached_deny = current_access, current_deny self.cache_valid = True current_access |= anon if access & current_deny or deny & current_access: raise NFS4Error(error)
def has_permission(self, access): """Verify access against current share""" if (not self.share_access) or \ (access == OPEN4_SHARE_ACCESS_WRITE and not (self.share_access & OPEN4_SHARE_ACCESS_WRITE)): raise NFS4Error(NFS4ERR_OPENMODE)
def find_state(env, stateid, allow_0=True, allow_bypass=False): """Find the matching StateTableEntry, and manage its lock.""" anon = False if env.is_ds: # STUB - have dataservers ignore stateid (but still do needed locking stateid = stateid4(0, DS_MAGIC * 12) state = env.cfh.state.types[ANON][(DS_MAGIC, )] # Could meddle with state.other here if needed anon = True # First we convert special stateids, see draft22 8.2.3 if stateid.other == "\0" * 12: if allow_0 and stateid.seqid == 0: state = env.cfh.state.anon0 anon = True elif stateid.seqid == 1: stateid = env.cid # Special stateids must be passed in explicitly if stateid in [None, nfs4lib.state00, nfs4lib.state11]: raise NFS4Error(NFS4ERR_BAD_STATEID, tag="Current stateid not useable") else: raise NFS4Error(NFS4ERR_BAD_STATEID) elif stateid.other == "\xff" * 12: if allow_0 and stateid.seqid == 0xffffffff: stateid = nfs4lib.state00 # Needed to pass seqid checks below state = (env.cfh.state.anon1 if allow_bypass else env.cfh.state.anon0) anon = True else: raise NFS4Error(NFS4ERR_BAD_STATEID) if not anon: # Now map stateid to find state state = env.session.client.state.get(stateid.other, None) if state is None: raise NFS4Error(NFS4ERR_BAD_STATEID, tag="stateid not known") if state.file != env.cfh: raise NFS4Error(NFS4ERR_BAD_STATEID, tag="cfh %r does not match stateid %r" % (state.file.fh, env.cfh.fh)) state.lock.acquire() # It is possible that while waiting to get the lock, the state has been # removed. In that case, the removal sets the invalid flag. if state.invalid: state.release() raise NFS4Error(NFS4ERR_BAD_STATEID, tag="stateid not known (race)") if state.type != LAYOUT: # See draft22 8.2.2 if stateid.seqid != 0 and stateid.seqid != state.seqid: old = (stateid.seqid < state.seqid) state.lock.release() if old: raise NFS4Error(NFS4ERR_OLD_STATEID, tag="bad stateid.seqid") else: raise NFS4Error(NFS4ERR_BAD_STATEID, tag="bad stateid.seqid") else: # See draft22 12.5.3 if stateid.seqid == 0: state.lock.release() raise NFS4Error(NFS4ERR_BAD_STATEID, tag="layout stateid.seqid==0") try: yield state finally: state.lock.release()