def randombytes(): """ <Purpose> Return a string of random bytes with length 1024 <Arguments> None. <Exceptions> None. <Side Effects> This function is metered because it may involve using a hardware source of randomness. <Resource Consumption> This operation consumes 1024 bytes of random data. <Returns> The string of bytes. """ # Wait for random resources nanny.tattle_quantity('random', 0) # If an OS-specific source of randomness is not a found # a NotImplementedError would be raised. # Anthony - a NotImplementedError will be logged as an internal # error so that we will hopefully be able to identify the system, # the exception is not passed on because the problem was not # caused by the user. The exit code 217 was chosen to be # unique from all other exit calls in repy. try: randomdata = os.urandom(1024) except NotImplementedError, e: tracebackrepy.handle_internalerror("os.urandom is not implemented " + \ "(Exception was: %s)" % e.message, 217)
def listfiles(): """ <Purpose> Allows the user program to get a list of files in their vessel. <Arguments> None <Exceptions> None <Side Effects> None <Resource Consumption> Consumes 4K of fileread. <Returns> A list of strings (file names) """ # We will consume 4K of fileread nanny.tattle_quantity('fileread', 4096) # Get the list of files from the current directory files = os.listdir(repy_constants.REPY_CURRENT_DIR) # Return the files return files
def _read(devs, timeout=None): ready = select.select(devs, [], [], timeout)[0] if ready: dev = ready[0] nanny.tattle_quantity("netrecv", 0) data = dev.read() nanny.tattle_quantity("netrecv", len(data)) return (dev.alias, data) raise TimeoutError("No data read")
def write(self, writeitem): # block if already over nanny.tattle_quantity('lograte', 0) # do the actual write loggingrepy_core.flush_logger_core.write(self, writeitem) # block if over after log write writeamt = len(str(writeitem)) nanny.tattle_quantity('lograte', writeamt)
def removefile(filename): """ <Purpose> Allows the user program to remove a file in their area. <Arguments> filename: the name of the file to remove. It must not contain characters other than 'a-zA-Z0-9.-_' and cannot be '.', '..' or the empty string. <Exceptions> RepyArgumentError is raised if the filename is invalid. FileInUseError is raised if the file is already open. FileNotFoundError is raised if the file does not exist <Side Effects> None <Resource Consumption> Consumes 4K of fileread. If successful, also consumes 4K of filewrite. <Returns> None """ # Raise an RepyArgumentError if the filename isn't valid # If the filename is invalid, it will raise an FileNotFoundError, why do # an explicit check. # _assert_is_allowed_filename(filename) OPEN_FILES_LOCK.acquire() try: # Consume the filewrite resources nanny.tattle_quantity('filewrite', 4096) # Don't explicitly check for state of the file like os.isfile(), if the file is # opened or not when removing. Let python handle that stuff, we will catch exceptions. # This way if everything is good os.remove() is called immediatly. # Remove the file (failure is an internal error) os.remove(filename) # I need to catch two separate Errors, to maintain portability between windows and linux # even though these checks take a bit more time, these are only executed if there's an # error, and I think nobody will see a performance issue here. except OSError: raise FileNotFoundError('Cannot remove non-existent file "' + filename + '".') except WindowsError, err: if err.winerror == 2: raise FileNotFoundError('Cannot remove non-existent file "' + filename + '".') elif err.winerror == 32: raise FileInUseError('Cannot remove file "' + filename + '" because it is in use!')
def writelines(self, writelist): # block if already over nanny.tattle_quantity('lograte', 0) # do the actual writelines() loggingrepy_core.flush_logger_core.writelines(self, writelist) # block if over after log write writeamt = 0 for writeitem in writelist: writeamt = writeamt + len(str(writeitem)) nanny.tattle_quantity('lograte', writeamt)
def removefile(filename): """ <Purpose> Allows the user program to remove a file in their area. <Arguments> filename: the name of the file to remove. It must not contain characters other than 'a-zA-Z0-9.-_' and cannot be '.', '..' or the empty string. <Exceptions> RepyArgumentError is raised if the filename is invalid. FileInUseError is raised if the file is already open. FileNotFoundError is raised if the file does not exist <Side Effects> None <Resource Consumption> Consumes 4K of fileread. If successful, also consumes 4K of filewrite. <Returns> None """ # raise an RepyArgumentError if the filename isn't valid _assert_is_allowed_filename(filename) OPEN_FILES_LOCK.acquire() try: # Check if the file is in use if filename in OPEN_FILES: raise FileInUseError('Cannot remove file "' + filename + '" because it is in use!') # Get the absolute file name absolute_filename = os.path.abspath( os.path.join(repy_constants.REPY_CURRENT_DIR, filename)) # Check if the file exists nanny.tattle_quantity('fileread', 4096) if not os.path.isfile(absolute_filename): raise FileNotFoundError('Cannot remove non-existent file "' + filename + '".') # Consume the filewrite resources nanny.tattle_quantity('filewrite', 4096) # Remove the file (failure is an internal error) os.remove(absolute_filename) finally: OPEN_FILES_LOCK.release()
def removefile(filename): """ <Purpose> Allows the user program to remove a file in their area. <Arguments> filename: the name of the file to remove. It must not contain characters other than 'a-zA-Z0-9.-_' and cannot be '.', '..' or the empty string. <Exceptions> RepyArgumentError is raised if the filename is invalid. FileInUseError is raised if the file is already open. FileNotFoundError is raised if the file does not exist <Side Effects> None <Resource Consumption> Consumes 4K of fileread. If successful, also consumes 4K of filewrite. <Returns> None """ # Raise an RepyArgumentError if the filename isn't valid # If the filename is invalid, it will raise an FileNotFoundError, why do # an explicit check. # _assert_is_allowed_filename(filename) OPEN_FILES_LOCK.acquire() try: # Consume the filewrite resources nanny.tattle_quantity('filewrite',4096) # Don't explicitly check for state of the file like os.isfile(), if the file is # opened or not when removing. Let python handle that stuff, we will catch exceptions. # This way if everything is good os.remove() is called immediatly. # Remove the file (failure is an internal error) os.remove(filename) # I need to catch two separate Errors, to maintain portability between windows and linux # even though these checks take a bit more time, these are only executed if there's an # error, and I think nobody will see a performance issue here. except OSError: raise FileNotFoundError('Cannot remove non-existent file "'+filename+'".') except WindowsError, err: if err.winerror == 2: raise FileNotFoundError('Cannot remove non-existent file "'+filename+'".') elif err.winerror == 32: raise FileInUseError('Cannot remove file "'+filename+'" because it is in use!')
def removefile(filename): """ <Purpose> Allows the user program to remove a file in their area. <Arguments> filename: the name of the file to remove. It must not contain characters other than 'a-z0-9.-_' and cannot start with a period or the empty string. <Exceptions> RepyArgumentError is raised if the filename is invalid. FileInUseError is raised if the file is already open. FileNotFoundError is raised if the file does not exist <Side Effects> None <Resource Consumption> Consumes 4K of fileread. If successful, also consumes 4K of filewrite. <Returns> None """ # raise an RepyArgumentError if the filename isn't valid _assert_is_allowed_filename(filename) OPEN_FILES_LOCK.acquire() try: # Check if the file is in use if filename in OPEN_FILES: raise FileInUseError('Cannot remove file "'+filename+'" because it is in use!') # Get the absolute file name absolute_filename = os.path.abspath(os.path.join(repy_constants.REPY_CURRENT_DIR, filename)) # Check if the file exists nanny.tattle_quantity('fileread', 4096) if not os.path.isfile(absolute_filename): raise FileNotFoundError('Cannot remove non-existent file "'+filename+'".') # Consume the filewrite resources nanny.tattle_quantity('filewrite',4096) # Remove the file (failure is an internal error) os.remove(absolute_filename) finally: OPEN_FILES_LOCK.release()
def next(self): # prevent TOCTOU race with client changing my filehandle myfilehandle = self.filehandle restrictions.assertisallowed('file.next') if "w" in self.mode: raise IOError("file.next() is invalid for write-enabled files.") # wait if it's already over used nanny.tattle_quantity('fileread',0) readdata = fileinfo[myfilehandle]['fobj'].next() nanny.tattle_quantity('fileread', len(readdata)) return readdata
def readline(self, *args): # prevent TOCTOU race with client changing my filehandle myfilehandle = self.filehandle restrictions.assertisallowed("file.readline", *args) # wait if it's already over used nanny.tattle_quantity("fileread", 0) try: readdata = fileinfo[myfilehandle]["fobj"].readline(*args) except KeyError: raise ValueError("Invalid file object (probably closed).") nanny.tattle_quantity("fileread", len(readdata)) return readdata
def next(self): # prevent TOCTOU race with client changing my filehandle myfilehandle = self.filehandle restrictions.assertisallowed("file.next") if "w" in self.mode: raise IOError("file.next() is invalid for write-enabled files.") # wait if it's already over used nanny.tattle_quantity("fileread", 0) readdata = fileinfo[myfilehandle]["fobj"].next() nanny.tattle_quantity("fileread", len(readdata)) return readdata
def readline(self,*args): # prevent TOCTOU race with client changing my filehandle myfilehandle = self.filehandle restrictions.assertisallowed('file.readline',*args) # wait if it's already over used nanny.tattle_quantity('fileread',0) try: readdata = fileinfo[myfilehandle]['fobj'].readline(*args) except KeyError: raise ValueError("Invalid file object (probably closed).") nanny.tattle_quantity('fileread',len(readdata)) return readdata
def randomfloat(): """ <Purpose> Return a random number in the range [0.0, 1.0) using sources provided by the operating system (such as /dev/urandom on Unix or CryptGenRandom on Windows). <Arguments> None <Exceptions> None <Side Effects> This function is metered because it may involve using a hardware source of randomness. If os.urandom raises a NotImplementedError then we will log the exception as interalerror and a harshexit will occur. A machine that raised this exception has not been observed but it is best that the problemed be logged. os.urandom will raise the exception if a source of OS-specific random numbers is not found. <Returns> The number (a float) """ restrictions.assertisallowed('randomfloat') nanny.tattle_quantity('random', 1) # If an OS-specific source of randomness is not a found # a NotImplementedError would be raised. # Anthony - a NotImplementedError will be logged as an internal # error so that we will hopefully be able to identify the system, # the exception is not passed on because the problem was not # caused by the user. The exit code 217 was chosen to be # unique from all other exit calls in repy. # Get 56 bits of random data try: randombytes = os.urandom(7) except NotImplementedError, e: tracebackrepy.handle_internalerror("os.urandom is not implemented " + \ "(Exception was: %s)" % e.message, 217)
def randomfloat(): """ <Purpose> Return a random number in the range [0.0, 1.0) using sources provided by the operating system (such as /dev/urandom on Unix or CryptGenRandom on Windows). <Arguments> None <Exceptions> None <Side Effects> This function is metered because it may involve using a hardware source of randomness. If os.urandom raises a NotImplementedError then we will log the exception as interalerror and a harshexit will occur. A machine that raised this exception has not been observed but it is best that the problemed be logged. os.urandom will raise the exception if a source of OS-specific random numbers is not found. <Returns> The number (a float) """ restrictions.assertisallowed('randomfloat') nanny.tattle_quantity('random',1) # If an OS-specific source of randomness is not a found # a NotImplementedError would be raised. # Anthony - a NotImplementedError will be logged as an internal # error so that we will hopefully be able to identify the system, # the exception is not passed on because the problem was not # caused by the user. The exit code 217 was chosen to be # unique from all other exit calls in repy. # Get 56 bits of random data try: randombytes = os.urandom(7) except NotImplementedError, e: tracebackrepy.handle_internalerror("os.urandom is not implemented " + \ "(Exception was: %s)" % e.message, 217)
def write(self, writeitem): # they / we can always log info (or else what happens on exception?) # acquire (and release later no matter what) self.writelock.acquire() try: if self.should_nanny: # Only invoke the nanny if the should_nanny flag is set. # block if already over nanny.tattle_quantity('lograte',0) writeamt = self.writedata(writeitem) if self.should_nanny: # Only invoke the nanny if the should_nanny flag is set. nanny.tattle_quantity('lograte',writeamt) finally: self.writelock.release()
def write(self, writeitem): # they / we can always log info (or else what happens on exception?) # acquire (and release later no matter what) self.writelock.acquire() try: if self.should_nanny: # Only invoke the nanny if the should_nanny flag is set. # block if already over nanny.tattle_quantity('lograte', 0) writeamt = self.writedata(writeitem) if self.should_nanny: # Only invoke the nanny if the should_nanny flag is set. nanny.tattle_quantity('lograte', writeamt) finally: self.writelock.release()
def write(self,writeitem): # prevent TOCTOU race with client changing my filehandle myfilehandle = self.filehandle restrictions.assertisallowed('file.write',writeitem) # wait if it's already over used nanny.tattle_quantity('filewrite',0) if "w" in self.mode: try: retval = fileinfo[myfilehandle]['fobj'].write(writeitem) except KeyError: raise ValueError("Invalid file object (probably closed).") else: raise ValueError("write() isn't allowed on read-only file objects!") writeamt = len(str(writeitem)) nanny.tattle_quantity('filewrite',writeamt) return retval
def write(self, writeitem): # prevent TOCTOU race with client changing my filehandle myfilehandle = self.filehandle restrictions.assertisallowed("file.write", writeitem) # wait if it's already over used nanny.tattle_quantity("filewrite", 0) if "w" in self.mode: try: retval = fileinfo[myfilehandle]["fobj"].write(writeitem) except KeyError: raise ValueError("Invalid file object (probably closed).") else: raise ValueError("write() isn't allowed on read-only file objects!") writeamt = len(str(writeitem)) nanny.tattle_quantity("filewrite", writeamt) return retval
def writelines(self, writelist): # prevent TOCTOU race with client changing my filehandle myfilehandle = self.filehandle restrictions.assertisallowed("file.writelines", writelist) # wait if it's already over used nanny.tattle_quantity("filewrite", 0) if "w" not in self.mode: raise ValueError("writelines() isn't allowed on read-only file objects!") try: fh = fileinfo[myfilehandle]["fobj"] except KeyError: raise ValueError("Invalid file object (probably closed).") for writeitem in writelist: strtowrite = str(writeitem) fileinfo[myfilehandle]["fobj"].write(strtowrite) nanny.tattle_quantity("filewrite", len(strtowrite)) return None # python documentation states there is no return value
def writelines(self, writelist): # we / they can always log info (or else what happens on exception?) #restrictions.assertisallowed('log.writelines',writelist) # acquire (and release later no matter what) self.writelock.acquire() try: if self.should_nanny: # Only invoke the nanny if the should_nanny flag is set. # block if already over nanny.tattle_quantity('lograte',0) writeamt = 0 for writeitem in writelist: writeamt = writeamt + self.writedata(writeitem) if self.should_nanny: # Only invoke the nanny if the should_nanny flag is set. nanny.tattle_quantity('lograte',writeamt) finally: self.writelock.release()
def writelines(self,writelist): # prevent TOCTOU race with client changing my filehandle myfilehandle = self.filehandle restrictions.assertisallowed('file.writelines',writelist) # wait if it's already over used nanny.tattle_quantity('filewrite',0) if "w" not in self.mode: raise ValueError("writelines() isn't allowed on read-only file objects!") try: fh = fileinfo[myfilehandle]['fobj'] except KeyError: raise ValueError("Invalid file object (probably closed).") for writeitem in writelist: strtowrite = str(writeitem) fileinfo[myfilehandle]['fobj'].write(strtowrite) nanny.tattle_quantity('filewrite', len(strtowrite)) return None # python documentation states there is no return value
def read(self, *args): # prevent TOCTOU race with client changing my filehandle myfilehandle = self.filehandle restrictions.assertisallowed("file.read", *args) # basic sanity checking of input if len(args) > 1: raise TypeError("read() takes at most 1 argument") if len(args) == 1 and type(args[0]) != int: raise TypeError("file.read() expects an integer argument") # wait if it's already over used nanny.tattle_quantity("fileread", 0) try: readdata = fileinfo[myfilehandle]["fobj"].read(*args) except KeyError: raise ValueError("Invalid file object (probably closed).") nanny.tattle_quantity("fileread", len(readdata)) return readdata
def read(self,*args): # prevent TOCTOU race with client changing my filehandle myfilehandle = self.filehandle restrictions.assertisallowed('file.read',*args) # basic sanity checking of input if len(args) > 1: raise TypeError("read() takes at most 1 argument") if len(args) == 1 and type(args[0]) != int: raise TypeError("file.read() expects an integer argument") # wait if it's already over used nanny.tattle_quantity('fileread',0) try: readdata = fileinfo[myfilehandle]['fobj'].read(*args) except KeyError: raise ValueError("Invalid file object (probably closed).") nanny.tattle_quantity('fileread',len(readdata)) return readdata
# If an OS-specific source of randomness is not a found # a NotImplementedError would be raised. # Anthony - a NotImplementedError will be logged as an internal # error so that we will hopefully be able to identify the system, # the exception is not passed on because the problem was not # caused by the user. The exit code 217 was chosen to be # unique from all other exit calls in repy. try: randomdata = os.urandom(1024) except NotImplementedError, e: tracebackrepy.handle_internalerror("os.urandom is not implemented " + \ "(Exception was: %s)" % e.message, 217) # Tattle all 1024 now nanny.tattle_quantity('random', 1024) return randomdata def getruntime(): """ <Purpose> Return the amount of time the program has been running. This is in wall clock time. This is guaranteed to be monotonic. <Arguments> None <Exceptions> None.
# If an OS-specific source of randomness is not a found # a NotImplementedError would be raised. # Anthony - a NotImplementedError will be logged as an internal # error so that we will hopefully be able to identify the system, # the exception is not passed on because the problem was not # caused by the user. The exit code 217 was chosen to be # unique from all other exit calls in repy. try: randomdata = os.urandom(1024) except NotImplementedError, e: tracebackrepy.handle_internalerror("os.urandom is not implemented " + \ "(Exception was: %s)" % e.message, 217) # Tattle all 1024 now nanny.tattle_quantity('random',1024) return randomdata def getruntime(): """ <Purpose> Return the amount of time the program has been running. This is in wall clock time. This is guaranteed to be monotonic. <Arguments> None <Exceptions> None.
def readat(self,sizelimit,offset): """ <Purpose> Reads from a file handle. Reading 0 bytes informs you if you have read past the end-of-file, but returns no data. <Arguments> sizelimit: The maximum number of bytes to read from the file. Reading EOF will read less. By setting this value to None, the entire file is read. offset: Seek to a specific absolute offset before reading. <Exceptions> RepyArgumentError is raised if the offset or size is negative. FileClosedError is raised if the file is already closed. SeekPastEndOfFileError is raised if trying to read past the end of the file. <Resource Consumption> Consumes 4K of fileread for each 4K aligned-block of the file read. All reads will consume at least 4K. <Returns> The data that was read. This may be the empty string if we have reached the end of the file, or if the sizelimit was 0. """ # Check the arguments if sizelimit < 0 and sizelimit != None: raise RepyArgumentError("Negative sizelimit specified!") if offset < 0: raise RepyArgumentError("Negative read offset speficied!") # Get the seek lock self.seek_lock.acquire() try: # Get the underlying file object fobj = self.fobj if fobj is None: raise FileClosedError("File '"+self.filename+"' is already closed!") # Check the provided offset if offset > self.filesize: raise SeekPastEndOfFileError("Seek offset extends past the EOF!") # Seek to the correct location fobj.seek(offset) # Wait for available file read resources nanny.tattle_quantity('fileread',0) if sizelimit != None: # Read the data data = fobj.read(sizelimit) else: # read all the data... data = fobj.read() finally: # Release the seek lock self.seek_lock.release() # Check how much we've read, in terms of 4K "blocks" end_offset = len(data) + offset disk_blocks_read = end_offset / 4096 - offset / 4096 if end_offset % 4096 > 0: disk_blocks_read += 1 # Charge 4K per block nanny.tattle_quantity('fileread', disk_blocks_read*4096) # Return the data return data
def __init__(self, filename, create): """ This is an internal initializer. See emulated_open for details. """ # Initialize the fields, otherwise __del__ gets confused # when we throw an exception. This was not a problem when the # logic was in emulated_open, since we would never throw an # exception self.filename = filename self.abs_filename = None self.fobj = None self.seek_lock = threading.Lock() self.filesize = 0 # raise an RepyArgumentError if the filename isn't valid _assert_is_allowed_filename(filename) # Check the type of create if type(create) is not bool: raise RepyArgumentError("Create argument type is invalid! Must be a Boolean!") OPEN_FILES_LOCK.acquire() try: # Check if the file is in use if filename in OPEN_FILES: raise FileInUseError('Cannot open file "'+filename+'" because it is already open!') # Get the absolute file name self.abs_filename = os.path.abspath(os.path.join(repy_constants.REPY_CURRENT_DIR, filename)) # Here is where we try to allocate a "file" resource from the # nanny system. We will restore this below if there is an exception # This may raise a ResourceExhautedError nanny.tattle_add_item('filesopened', self.abs_filename) # charge for checking if the file exists. nanny.tattle_quantity('fileread', 4096) exists = os.path.isfile(self.abs_filename) # if there isn't a file already... if not exists: # if we shouldn't create it, it's an error if not create: raise FileNotFoundError('Cannot openfile non-existent file "'+filename+'" without creating it!') # okay, we should create it... nanny.tattle_quantity('filewrite', 4096) safe_open(self.abs_filename, "w").close() # Forces file creation # Store a file handle # Always open in mode r+b, this avoids Windows text-mode # quirks, and allows reading and writing self.fobj = safe_open(self.abs_filename, "r+b") # Add the filename to the open files OPEN_FILES.add(filename) # Get the file's size self.filesize = os.path.getsize(self.abs_filename) except RepyException: # Restore the file handle we tattled nanny.tattle_remove_item('filesopened', self.abs_filename) raise finally: OPEN_FILES_LOCK.release()
def __init__(self, filename, create): """ This is an internal initializer. See emulated_open for details. """ # Initialize the fields, otherwise __del__ gets confused # when we throw an exception. This was not a problem when the # logic was in emulated_open, since we would never throw an # exception self.filename = filename self.abs_filename = None self.fobj = None self.seek_lock = threading.Lock() self.filesize = 0 # raise an RepyArgumentError if the filename isn't valid _assert_is_allowed_filename(filename) # Check the type of create if type(create) is not bool: raise RepyArgumentError( "Create argument type is invalid! Must be a Boolean!") OPEN_FILES_LOCK.acquire() try: # Check if the file is in use if filename in OPEN_FILES: raise FileInUseError('Cannot open file "' + filename + '" because it is already open!') # Get the absolute file name self.abs_filename = os.path.abspath( os.path.join(repy_constants.REPY_CURRENT_DIR, filename)) # Here is where we try to allocate a "file" resource from the # nanny system. We will restore this below if there is an exception # This may raise a ResourceExhautedError nanny.tattle_add_item('filesopened', self.abs_filename) # charge for checking if the file exists. nanny.tattle_quantity('fileread', 4096) exists = os.path.isfile(self.abs_filename) # if there isn't a file already... if not exists: # if we shouldn't create it, it's an error if not create: raise FileNotFoundError( 'Cannot openfile non-existent file "' + filename + '" without creating it!') # okay, we should create it... nanny.tattle_quantity('filewrite', 4096) safe_open(self.abs_filename, "w").close() # Forces file creation # Store a file handle # Always open in mode r+b, this avoids Windows text-mode # quirks, and allows reading and writing self.fobj = safe_open(self.abs_filename, "r+b") # Add the filename to the open files OPEN_FILES.add(filename) # Get the file's size self.filesize = os.path.getsize(self.abs_filename) except RepyException: # Restore the file handle we tattled nanny.tattle_remove_item('filesopened', self.abs_filename) raise finally: OPEN_FILES_LOCK.release()
def writeat(self,data,offset): """ <Purpose> Allows the user program to write data to a file. <Arguments> data: The data to write offset: An absolute offset into the file to write <Exceptions> RepyArgumentError is raised if the offset is negative or the data is not a string. FileClosedError is raised if the file is already closed. SeekPastEndOfFileError is raised if trying to write past the EOF. <Side Effects> Writes to persistent storage. <Resource Consumption> Consumes 4K of filewrite for each 4K aligned-block of the file written. All writes consume at least 4K. <Returns> Nothing """ # Check the arguments if offset < 0: raise RepyArgumentError("Negative read offset speficied!") if type(data) is not str: raise RepyArgumentError("Data must be specified as a string!") # Get the seek lock self.seek_lock.acquire() try: # Get the underlying file object fobj = self.fobj if fobj is None: raise FileClosedError("File '"+self.filename+"' is already closed!") # Check the provided offset if offset > self.filesize: raise SeekPastEndOfFileError("Seek offset extends past the EOF!") # Seek to the correct location fobj.seek(offset) # Wait for available file write resources nanny.tattle_quantity('filewrite',0) # Write the data and flush to disk fobj.write(data) fobj.flush() # Check if we expanded the file size if offset + len(data) > self.filesize: self.filesize = offset + len(data) finally: # Release the seek lock self.seek_lock.release() # Check how much we've written, in terms of 4K "blocks" end_offset = len(data) + offset disk_blocks_written = end_offset / 4096 - offset / 4096 if end_offset % 4096 > 0: disk_blocks_written += 1 # Charge 4K per block nanny.tattle_quantity('filewrite', disk_blocks_written*4096)
def readat(self, sizelimit, offset): """ <Purpose> Reads from a file handle. Reading 0 bytes informs you if you have read past the end-of-file, but returns no data. <Arguments> sizelimit: The maximum number of bytes to read from the file. Reading EOF will read less. By setting this value to None, the entire file is read. offset: Seek to a specific absolute offset before reading. <Exceptions> RepyArgumentError is raised if the offset or size is negative. FileClosedError is raised if the file is already closed. SeekPastEndOfFileError is raised if trying to read past the end of the file. <Resource Consumption> Consumes 4K of fileread for each 4K aligned-block of the file read. All reads will consume at least 4K. <Returns> The data that was read. This may be the empty string if we have reached the end of the file, or if the sizelimit was 0. """ # Check the arguments if sizelimit < 0 and sizelimit != None: raise RepyArgumentError("Negative sizelimit specified!") if offset < 0: raise RepyArgumentError("Negative read offset speficied!") # Get the seek lock self.seek_lock.acquire() try: # Get the underlying file object fobj = self.fobj if fobj is None: raise FileClosedError("File '" + self.filename + "' is already closed!") # Check the provided offset if offset > self.filesize: raise SeekPastEndOfFileError( "Seek offset extends past the EOF!") # Seek to the correct location fobj.seek(offset) # Wait for available file read resources nanny.tattle_quantity('fileread', 0) if sizelimit != None: # Read the data data = fobj.read(sizelimit) else: # read all the data... data = fobj.read() finally: # Release the seek lock self.seek_lock.release() # Check how much we've read, in terms of 4K "blocks" end_offset = len(data) + offset disk_blocks_read = end_offset / 4096 - offset / 4096 if end_offset % 4096 > 0: disk_blocks_read += 1 # Charge 4K per block nanny.tattle_quantity('fileread', disk_blocks_read * 4096) # Return the data return data
def writeat(self, data, offset): """ <Purpose> Allows the user program to write data to a file. <Arguments> data: The data to write offset: An absolute offset into the file to write <Exceptions> RepyArgumentError is raised if the offset is negative or the data is not a string. FileClosedError is raised if the file is already closed. SeekPastEndOfFileError is raised if trying to write past the EOF. <Side Effects> Writes to persistent storage. <Resource Consumption> Consumes 4K of filewrite for each 4K aligned-block of the file written. All writes consume at least 4K. <Returns> Nothing """ # Check the arguments if offset < 0: raise RepyArgumentError("Negative read offset speficied!") if type(data) is not str: raise RepyArgumentError("Data must be specified as a string!") # Get the seek lock self.seek_lock.acquire() try: # Get the underlying file object fobj = self.fobj if fobj is None: raise FileClosedError("File '" + self.filename + "' is already closed!") # Check the provided offset if offset > self.filesize: raise SeekPastEndOfFileError( "Seek offset extends past the EOF!") # Seek to the correct location fobj.seek(offset) # Wait for available file write resources nanny.tattle_quantity('filewrite', 0) # Write the data and flush to disk fobj.write(data) fobj.flush() # Check if we expanded the file size if offset + len(data) > self.filesize: self.filesize = offset + len(data) finally: # Release the seek lock self.seek_lock.release() # Check how much we've written, in terms of 4K "blocks" end_offset = len(data) + offset disk_blocks_written = end_offset / 4096 - offset / 4096 if end_offset % 4096 > 0: disk_blocks_written += 1 # Charge 4K per block nanny.tattle_quantity('filewrite', disk_blocks_written * 4096)
def device_send(name, data): nanny.tattle_quantity("netsend", len(data)) _get_device(name).send(data)