#! /usr/bin/env python import random from optparse import OptionParser DEBUG = False def dprint(str): if DEBUG: print str printOps = True printState = True printFinal = True class bitmap: def __init__(self, size): self.size = size self.bmap = [] for num in range(size): self.bmap.append(0) def alloc(self): for num in range(len(self.bmap)): if self.bmap[num] == 0: self.bmap[num] = 1 return num return -1 def free(self, num): assert(self.bmap[num] == 1) self.bmap[num] = 0 def markAllocated(self, num): assert(self.bmap[num] == 0) self.bmap[num] = 1 def dump(self): s = '' for i in range(len(self.bmap)): s += str(self.bmap[i]) return s class block: def __init__(self, ftype): assert(ftype == 'd' or ftype == 'f' or ftype == 'free') self.ftype = ftype # only for directories, properly a subclass but who cares self.dirUsed = 0 self.maxUsed = 32 self.dirList = [] self.data = '' def dump(self): if self.ftype == 'free': return '[]' elif self.ftype == 'd': rc = '' for d in self.dirList: # d is of the form ('name', inum) short = '(%s,%s)' % (d[0], d[1]) if rc == '': rc = short else: rc += ' ' + short return '['+rc+']' # return '%s' % self.dirList else: return '[%s]' % self.data def setType(self, ftype): assert(self.ftype == 'free') self.ftype = ftype def addData(self, data): assert(self.ftype == 'f') self.data = data def getNumEntries(self): assert(self.ftype == 'd') return self.dirUsed def getFreeEntries(self): assert(self.ftype == 'd') return self.maxUsed - self.dirUsed def getEntry(self, num): assert(self.ftype == 'd') assert(num < self.dirUsed) return self.dirList[num] def addDirEntry(self, name, inum): assert(self.ftype == 'd') self.dirList.append((name, inum)) self.dirUsed += 1 assert(self.dirUsed <= self.maxUsed) def delDirEntry(self, name): assert(self.ftype == 'd') tname = name.split('/') dname = tname[len(tname) - 1] for i in range(len(self.dirList)): if self.dirList[i][0] == dname: self.dirList.pop(i) self.dirUsed -= 1 return assert(1 == 0) def dirEntryExists(self, name): assert(self.ftype == 'd') for d in self.dirList: if name == d[0]: return True return False def free(self): assert(self.ftype != 'free') if self.ftype == 'd': # check for only dot, dotdot here assert(self.dirUsed == 2) self.dirUsed = 0 self.data = '' self.ftype = 'free' class inode: def __init__(self, ftype='free', addr=-1, refCnt=1): self.setAll(ftype, addr, refCnt) def setAll(self, ftype, addr, refCnt): assert(ftype == 'd' or ftype == 'f' or ftype == 'free') self.ftype = ftype self.addr = addr self.refCnt = refCnt def incRefCnt(self): self.refCnt += 1 def decRefCnt(self): self.refCnt -= 1 def getRefCnt(self): return self.refCnt def setType(self, ftype): assert(ftype == 'd' or ftype == 'f' or ftype == 'free') self.ftype = ftype def setAddr(self, block): self.addr = block def getSize(self): if self.addr == -1: return 0 else: return 1 def getAddr(self): return self.addr def getType(self): return self.ftype def free(self): self.ftype = 'free' self.addr = -1 class fs: def __init__(self, numInodes, numData): self.numInodes = numInodes self.numData = numData self.ibitmap = bitmap(self.numInodes) self.inodes = [] for i in range(self.numInodes): self.inodes.append(inode()) self.dbitmap = bitmap(self.numData) self.data = [] for i in range(self.numData): self.data.append(block('free')) # root inode self.ROOT = 0 # create root directory self.ibitmap.markAllocated(self.ROOT) self.inodes[self.ROOT].setAll('d', 0, 2) self.dbitmap.markAllocated(self.ROOT) self.data[0].setType('d') self.data[0].addDirEntry('.', self.ROOT) self.data[0].addDirEntry('..', self.ROOT) # these is just for the fake workload generator self.files = [] self.dirs = ['/'] self.nameToInum = {'/':self.ROOT} def dump(self): print 'inode bitmap ', self.ibitmap.dump() print 'inodes ', for i in range(0,self.numInodes): ftype = self.inodes[i].getType() if ftype == 'free': print '[]', else: print '[%s a:%s r:%d]' % (ftype, self.inodes[i].getAddr(), self.inodes[i].getRefCnt()), print '' print 'data bitmap ', self.dbitmap.dump() print 'data ', for i in range(self.numData): print self.data[i].dump(), print '' def makeName(self): p = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'j', 'k', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'] return p[int(random.random() * len(p))] def inodeAlloc(self): return self.ibitmap.alloc() def inodeFree(self, inum): self.ibitmap.free(inum) self.inodes[inum].free() def dataAlloc(self): return self.dbitmap.alloc() def dataFree(self, bnum): self.dbitmap.free(bnum) self.data[bnum].free() def getParent(self, name): tmp = name.split('/') if len(tmp) == 2: return '/' pname = '' for i in range(1, len(tmp)-1): pname = pname + '/' + tmp[i] return pname def deleteFile(self, tfile): if printOps: print 'unlink("%s");' % tfile inum = self.nameToInum[tfile] # YOUR CODE, YOUR ID # IF inode.refcnt ==1, THEN free data blocks first, then free inode, ELSE dec indoe.refcnt # remove from parent directory: delete from parent inum, delete from parent addr # DONE # finally, remove from files list self.files.remove(tfile) return 0 def createLink(self, target, newfile, parent): # YOUR CODE, YOUR ID # find info about parent # is there room in the parent directory? # if the newfile was already in parent dir? # now, find inumber of target # inc parent ref count # now add to directory # DONE return tinum def createFile(self, parent, newfile, ftype): # YOUR CODE, YOUR ID # find info about parent # is there room in the parent directory? # have to make sure file name is unique # find free inode # if a directory, have to allocate directory block for basic (., ..) info # now ok to init inode properly # inc parent ref count # and add to directory of parent # DONE return inum def writeFile(self, tfile, data): inum = self.nameToInum[tfile] curSize = self.inodes[inum].getSize() dprint('writeFile: inum:%d cursize:%d refcnt:%d' % (inum, curSize, self.inodes[inum].getRefCnt())) # YOUR CODE, YOUR ID # file is full? # no data blocks left # write file data # DONE if printOps: print 'fd=open("%s", O_WRONLY|O_APPEND); write(fd, buf, BLOCKSIZE); close(fd);' % tfile return 0 def doDelete(self): dprint('doDelete') if len(self.files) == 0: return -1 dfile = self.files[int(random.random() * len(self.files))] dprint('try delete(%s)' % dfile) return self.deleteFile(dfile) def doLink(self): dprint('doLink') if len(self.files) == 0: return -1 parent = self.dirs[int(random.random() * len(self.dirs))] nfile = self.makeName() # pick random target target = self.files[int(random.random() * len(self.files))] # get full name of newfile if parent == '/': fullName = parent + nfile else: fullName = parent + '/' + nfile dprint('try createLink(%s %s %s)' % (target, nfile, parent)) inum = self.createLink(target, nfile, parent) if inum >= 0: self.files.append(fullName) self.nameToInum[fullName] = inum if printOps: print 'link("%s", "%s");' % (target, fullName) return 0 return -1 def doCreate(self, ftype): dprint('doCreate') parent = self.dirs[int(random.random() * len(self.dirs))] nfile = self.makeName() if ftype == 'd': tlist = self.dirs else: tlist = self.files if parent == '/': fullName = parent + nfile else: fullName = parent + '/' + nfile dprint('try createFile(%s %s %s)' % (parent, nfile, ftype)) inum = self.createFile(parent, nfile, ftype) if inum >= 0: tlist.append(fullName) self.nameToInum[fullName] = inum if parent == '/': parent = '' if ftype == 'd': if printOps: print 'mkdir("%s/%s");' % (parent, nfile) else: if printOps: print 'creat("%s/%s");' % (parent, nfile) return 0 return -1 def doAppend(self): dprint('doAppend') if len(self.files) == 0: return -1 afile = self.files[int(random.random() * len(self.files))] dprint('try writeFile(%s)' % afile) data = chr(ord('a') + int(random.random() * 26)) rc = self.writeFile(afile, data) return rc def run(self, numRequests): self.percentMkdir = 0.40 self.percentWrite = 0.40 self.percentDelete = 0.20 self.numRequests = numRequests print 'Initial state' print '' self.dump() print '' for i in range(numRequests): if printOps == False: print 'Which operation took place?' rc = -1 while rc == -1: r = random.random() if r < 0.3: rc = self.doAppend() dprint('doAppend rc:%d' % rc) elif r < 0.5: rc = self.doDelete() dprint('doDelete rc:%d' % rc) elif r < 0.7: rc = self.doLink() dprint('doLink rc:%d' % rc) else: if random.random() < 0.75: rc = self.doCreate('f') dprint('doCreate(f) rc:%d' % rc) else: rc = self.doCreate('d') dprint('doCreate(d) rc:%d' % rc) if printState == True: print '' self.dump() print '' else: print '' print ' State of file system (inode bitmap, inodes, data bitmap, data)?' print '' if printFinal: print '' print 'Summary of files, directories::' print '' print ' Files: ', self.files print ' Directories:', self.dirs print '' # # main program # parser = OptionParser() parser.add_option('-s', '--seed', default=0, help='the random seed', action='store', type='int', dest='seed') parser.add_option('-i', '--numInodes', default=8, help='number of inodes in file system', action='store', type='int', dest='numInodes') parser.add_option('-d', '--numData', default=8, help='number of data blocks in file system', action='store', type='int', dest='numData') parser.add_option('-n', '--numRequests', default=10, help='number of requests to simulate', action='store', type='int', dest='numRequests') parser.add_option('-r', '--reverse', default=False, help='instead of printing state, print ops', action='store_true', dest='reverse') parser.add_option('-p', '--printFinal', default=False, help='print the final set of files/dirs', action='store_true', dest='printFinal') (options, args) = parser.parse_args() print 'ARG seed', options.seed print 'ARG numInodes', options.numInodes print 'ARG numData', options.numData print 'ARG numRequests', options.numRequests print 'ARG reverse', options.reverse print 'ARG printFinal', options.printFinal print '' random.seed(options.seed) if options.reverse: printState = False printOps = True else: printState = True printOps = False printOps = True printState = True printFinal = options.printFinal # # have to generate RANDOM requests to the file system # that are VALID! # f = fs(options.numInodes, options.numData) # # ops: mkdir rmdir : create delete : append write # f.run(options.numRequests)