Beispiel #1
0
class TestGenericFile(unittest.TestCase):
    def setUp(self):
        Configuration.FILEPATH = 'test/resources/conf/mongofs.json'
        self.mongo = Mongo()
        GenericFile.mongo = self.mongo
        GenericFile.configuration = Configuration()
        self.utils = Utils(mongo=self.mongo)
        self.utils.load_files()

    def tearDown(self):
        self.mongo.clean_database()

    def test_get_target(self):
        self.assertEqual(self.utils.symbolic_link.get_target(),
                         self.utils.symbolic_link_raw['target'])

    def test_is_file(self):
        self.assertFalse(self.utils.symbolic_link.is_file())

    def test_is_dir(self):
        self.assertFalse(self.utils.symbolic_link.is_dir())

    def test_is_link(self):
        self.assertTrue(self.utils.symbolic_link.is_link())
        self.assertFalse(self.utils.directory.is_link())
        self.assertFalse(self.utils.file.is_link())
        self.assertFalse(self.utils.directory_file.is_link())
Beispiel #2
0
 def setUp(self):
     Configuration.FILEPATH = 'test/resources/conf/mongofs.json'
     self.obj = Mongo()
     GenericFile.mongo = self.obj
     GenericFile.configuration = Configuration()
     self.utils = Utils(mongo=self.obj)
     self.utils.load_files()
 def setUp(self):
     Configuration.FILEPATH = 'test/resources/conf/mongofs.json'
     self.mongo = Mongo(do_clean_up=True)
     GenericFile.mongo = self.mongo
     GenericFile.configuration = Configuration()
     self.utils = Utils(mongo=self.mongo)
     self.utils.load_files()
Beispiel #4
0
    def __init__(self):
        self.configuration = Configuration()
        self.mongo = Mongo()
        self.files = {}

        # Additional setup
        GenericFile.mongo = self.mongo
        GenericFile.configuration = self.configuration

        if self.configuration.is_development():
            # START DEBUG ONLY - Drop the old information from MongoDB
            self.mongo.clean_database()
            self.mongo = Mongo() # Create a new instance to be sure to have the top folder
Beispiel #5
0
class TestRootMode(unittest.TestCase):
    def setUpConfig(self, path):
        Configuration.FILEPATH = path
        GenericFile.configuration = Configuration()
        self.obj = Mongo(do_clean_up=True)
        GenericFile.mongo = self.obj

    def tearDown(self):
        self.obj.clean_database()

    def test_default_rootMode(self):
        self.setUpConfig('test/resources/conf/mongofs.json')
        self.assertEqual(GenericFile.configuration.default_root_mode(), 0o755)
        self.assertEqual(GenericFile.configuration.force_root_mode(), False)
        root = self.obj.get_generic_file(filepath='/')
        self.assertEqual(root.metadata['st_mode'] & 0o777, 0o755)
        self.assertEqual(root.metadata['st_mode'] & S_IFDIR, S_IFDIR)

    def test_special_rootMode(self):
        self.setUpConfig('test/resources/conf/mongofs-rootMode777.json')
        self.assertEqual(GenericFile.configuration.default_root_mode(), 0o777)
        self.assertEqual(GenericFile.configuration.force_root_mode(), True)
        root = self.obj.get_generic_file(filepath='/')
        self.assertEqual(root.metadata['st_mode'] & 0o777, 0o777)
        self.assertEqual(root.metadata['st_mode'] & S_IFDIR, S_IFDIR)

    def test_force_rootMode(self):
        self.setUpConfig('test/resources/conf/mongofs.json')
        root = self.obj.get_generic_file(filepath='/')
        self.assertEqual(root.metadata['st_mode'] & 0o777, 0o755)
        self.assertEqual(root.metadata['st_mode'] & S_IFDIR, S_IFDIR)

        # Now change to force mode to 777, it should update the existing data for /
        self.setUpConfig('test/resources/conf/mongofs-rootMode777.json')
        root = self.obj.get_generic_file(filepath='/')
        self.assertEqual(root.metadata['st_mode'] & 0o777, 0o777)
        self.assertEqual(root.metadata['st_mode'] & S_IFDIR, S_IFDIR)
Beispiel #6
0
class TestMongo(unittest.TestCase):
    def setUp(self):
        Configuration.FILEPATH = 'test/resources/conf/mongofs.json'
        self.obj = Mongo()
        GenericFile.mongo = self.obj
        GenericFile.configuration = Configuration()
        self.utils = Utils(mongo=self.obj)
        self.utils.load_files()

    def tearDown(self):
        self.obj.clean_database()

    def test_load_generic_file_file(self):
        self.assertIsInstance(self.obj.load_generic_file(self.utils.file_raw), File)

    def test_load_generic_file_directory(self):
        self.assertIsInstance(self.obj.load_generic_file(self.utils.directory_raw), Directory)

    def test_load_generic_file_symbolic_link(self):
        self.assertIsInstance(self.obj.load_generic_file(self.utils.symbolic_link_raw), SymbolicLink)

    def test_current_user(self):
        user = self.obj.current_user()
        self.assertIsInstance(user['uid'], int)
        self.assertIsInstance(user['gid'], int)
        self.assertIsInstance(user['pid'], int)

    def test_lock_id(self):
        filepath = 'test-file'
        hostname = Mongo.configuration.hostname()
        pid = self.obj.current_user()['pid']
        expected_lock = filepath+';'+str(pid)+';'+hostname
        self.assertEqual(self.obj.lock_id(filepath=filepath), expected_lock)

    def test_master_lock_id(self):
        filepath = 'test-file'
        hostname = Mongo.configuration.hostname()
        pid = 0
        expected_lock = filepath + ';' + str(pid) + ';' + hostname
        self.assertEqual(self.obj.master_lock_id(filepath=filepath), expected_lock)

    def test_create_generic_file(self):
        self.utils.insert_file()
        gf = self.utils.files_coll.find_one({'directory_id':self.utils.file.directory_id,'filename':self.utils.file.filename},{'uploadDate':False})
        self.assertEqual(json_util.dumps(gf), json_util.dumps(self.utils.file_raw))

    def test_remove_generic_file(self):
        self.utils.insert_file()
        self.obj.remove_generic_file(generic_file=self.utils.file)
        gf = self.utils.files_coll.find_one({'filename': self.utils.file.filename})
        self.assertEqual(gf, None)

    def test_remove_generic_file_directory_not_empty(self):
        # Try to delete the parent directory while a file still exist in it
        self.utils.insert_directory()
        self.obj.create_generic_file(generic_file=self.utils.directory_file)
        try:
            self.obj.remove_generic_file(generic_file=self.utils.directory)
            self.assertTrue(False, msg="It was possible to remove a directory while it was still containing files.")
        except FuseOSError as e:
            self.assertTrue(True)

    def test_remove_generic_file_directory_empty(self):
        # Try to delete the parent directory after deleting the file in it
        self.utils.insert_directory()
        self.utils.insert_directory_file()
        self.obj.remove_generic_file(generic_file=self.utils.directory_file)
        self.obj.remove_generic_file(generic_file=self.utils.directory)
        self.assertTrue(True)

    def test_list_generic_files_in_directory(self):
        self.utils.insert_directory()
        self.utils.insert_file()
        self.utils.insert_symbolic_link()

        files = self.obj.list_generic_files_in_directory(filepath='/')
        self.assertEqual(len(files), 3)

    def test_generic_file_exists(self):
        self.assertFalse(self.obj.generic_file_exists(self.utils.file.filepath))
        self.utils.insert_file()
        self.assertTrue(self.obj.generic_file_exists(self.utils.file.filepath))

    def test_get_generic_file(self):
        self.utils.insert_file()
        gf = self.obj.get_generic_file(filepath=self.utils.file.filepath)
        self.assertIsInstance(gf, File)

    def test_get_generic_file_take_lock(self):
        self.utils.insert_file()
        gf = self.obj.get_generic_file(filepath=self.utils.file.filepath, take_lock=True)
        self.assertIsInstance(gf, File)

        # We are the same owner, so normally, we should still be able to take the file if there is a lock on it.
        gf = self.obj.get_generic_file(filepath=self.utils.file.filepath, take_lock=True)
        self.assertIsInstance(gf, File)

    def test_get_generic_file_missing(self):
        gf = self.obj.get_generic_file(filepath=self.utils.file.filepath)
        self.assertEqual(gf, None)

    def test_add_nlink_directory(self):
        # By default, a directory has 2 st_nlink. And by default, the "/" directory always exists.
        self.obj.add_nlink_directory(directory_id=self.utils.root_id, value=4)
        gf = self.utils.files_coll.find_one({'_id': self.utils.root_id})
        self.assertEqual(gf['metadata']['st_nlink'], 6)

    def test_read_data(self):
        self.utils.insert_file()
        self.utils.insert_file_chunks()
        message = self.utils.read_file_chunks()

        data = self.obj.read_data(file=self.utils.file, offset=0, size=4096)
        self.assertEqual(data, message)

        data = self.obj.read_data(file=self.utils.file, offset=3, size=4096)
        self.assertEqual(data, message[3:])

        data = self.obj.read_data(file=self.utils.file, offset=0, size=8)
        self.assertEqual(data, message[:8])

        data = self.obj.read_data(file=self.utils.file, offset=3, size=8)
        self.assertEqual(data, message[3:3+8])

    def test_add_data_append(self):
        self.utils.insert_file()
        self.utils.insert_file_chunks()
        message = self.utils.read_file_chunks()

        self.obj.add_data(file=self.utils.file, data=b'test', offset=len(message))
        modified_message = self.utils.read_file_chunks()
        self.assertEqual(modified_message, message+b'test')

    def test_add_data_replace(self):
        self.utils.insert_file()
        self.utils.insert_file_chunks()
        message = self.utils.read_file_chunks()
        message = list(message)
        message[2] = ord('t')
        message[3] = ord('h')
        message[4] = ord('i')
        message[5] = ord('n')
        message[6] = ord('g')
        message[7] = ord('s')
        expected_message = ''.join(map(chr, message))

        self.obj.add_data(file=self.utils.file, data=b'things', offset=2)
        modified_message = self.utils.read_file_chunks()
        formatted_modified_message = ''.join(map(chr, list(modified_message)))
        self.assertEqual(formatted_modified_message, expected_message)

    def test_truncate(self):
        self.utils.insert_file()
        self.utils.insert_file_chunks()
        message = self.utils.read_file_chunks()

        self.obj.truncate(file=self.utils.file, length=6)
        modified_message = self.utils.read_file_chunks()
        self.assertEqual(modified_message, message[0:6])

    def test_truncate_zero(self):
        self.utils.insert_file()
        self.utils.insert_file_chunks()
        message = self.utils.read_file_chunks()

        self.obj.truncate(file=self.utils.file, length=0)
        modified_message = self.utils.read_file_chunks()
        self.assertEqual(modified_message, message[0:0])

    def test_rename_generic_file_to(self):
        self.utils.insert_file()
        self.utils.insert_file_chunks()
        message = self.utils.read_file_chunks()

        initial_filepath = self.utils.file.filepath
        self.obj.rename_generic_file_to(generic_file=self.utils.file, initial_filepath=self.utils.file.filepath, destination_filepath='/rename-test')
        destination_message = self.utils.read_file_chunks() # Read chunks is based on the _id, so we can call it
        # Normally, the chunks should not change at all, but we never know.
        self.assertEqual(destination_message, message)

        old_file = self.utils.files_coll.find_one({'directory_id':self.utils.file.directory_id,'filename':initial_filepath.split('/')[-1]})
        self.assertEqual(old_file, None)

        new_file = self.utils.files_coll.find_one({'directory_id':self.utils.root_id,'filename':'rename-test'})
        self.assertNotEqual(new_file, None)

    def test_unlock_generic_file(self):
        self.utils.insert_file()
        gf = self.obj.get_generic_file(filepath=self.utils.file.filepath, take_lock=True)
        result = self.obj.unlock_generic_file(filepath=self.utils.file.filepath, generic_file=gf)
        self.assertTrue(result)

        gf = self.obj.get_generic_file(filepath=self.utils.file.filepath)
        self.assertTrue('lock' not in gf.json)

    def test_unlock_generic_file_no_lock(self):
        # Verify if it does not crash if there is no lock
        self.utils.insert_file()
        gf = self.obj.get_generic_file(filepath=self.utils.file.filepath, take_lock=False)
        result = self.obj.unlock_generic_file(filepath=self.utils.file.filepath, generic_file=gf)
        self.assertTrue(result)

    def test_basic_save(self):
        self.utils.insert_file()
        self.obj.basic_save(generic_file=self.utils.file, metadata={'st_nlink':1}, attrs={'thing':1})
        result = self.utils.files_coll.find_one({'_id':self.utils.file._id})
        self.assertTrue('st_nlink' in result['metadata'] and len(result['metadata']) == 1)
        self.assertTrue('thing' in result['attrs'] and len(result['attrs']) == 1)
Beispiel #7
0
class MongoFS(LoggingMixIn, Operations):
    # This is useful to be able to umount if there is an error to access MongoDB for example
    mounting_point = None

    def __init__(self):
        self.configuration = Configuration()
        self.mongo = Mongo()
        self.files = {}

        # Additional setup
        GenericFile.mongo = self.mongo
        GenericFile.configuration = self.configuration

        if self.configuration.is_development():
            # START DEBUG ONLY - Drop the old information from MongoDB
            self.mongo.clean_database()
            self.mongo = Mongo() # Create a new instance to be sure to have the top folder
            # END DEBUG ONLY

        # The top folder of the FS is automatically created by the Mongo class.
    """
        Create a file and returns a "file descriptor", which is in fact, simply the _id.
    """
    def create(self, path, mode):
        file = GenericFile.new_generic_file(filepath=path, mode=mode, file_type=GenericFile.FILE_TYPE)
        return file.file_descriptor

    """
        Acquire a lock on a specific file.
    """
    def lock(self, path, fip, cmd, lock):
        # Getting the file object with a lock is enough to be sure there is one on it.
        self.mongo.get_generic_file(filepath=path, take_lock=True)
        return lock

    """
        Release the lock on a specific file.
    """
    def release(self, path, fh):
        print('Release the lock on '+str(path)+': '+str(fh))
        gf = self.mongo.get_generic_file(filepath=path)
        print('Before unlock: '+str(gf.lock))
        gf.unlock(filepath=path)
        return 0

    """
        Release the lock on a specific directory.
    """
    def releasedir(self, path, fh):
        gf = self.mongo.get_generic_file(filepath=path)
        gf.unlock(filepath=path)
        return 0

    """
        Rename a generic file
    """
    def rename(self, old, new):
        generic_file = self.mongo.get_generic_file(filepath=old)
        generic_file.rename_to(initial_filepath=old, destination_filepath=new)

    """
        Create a symbolic link to a generic file (source -> target). No need to return a file descriptor.
         target: the file we want to display if we display "source"
         source: the symbolic link itself
        Important: It seems we receive the symlink parameters in a different order than "ln -s TARGET SYMLINK",
        we receive them (SYMLINK, TARGET) which is kinda weird.
    """
    def symlink(self, source, target):
        GenericFile.new_generic_file(filepath=source, mode=0o777, file_type=GenericFile.SYMBOLIC_LINK_TYPE, target=target)

    """
        Read a symbolic link and return the file we should be redirected to.
    """
    def readlink(self, path):
        link = self.mongo.get_generic_file(filepath=path)
        return link.get_target()

    """
        Read a part of a file
    """
    def read(self, path, size, offset, fh):
        file = self.mongo.get_generic_file(filepath=path)
        tmp =  file.read_data(offset=offset, size=size)
        return tmp

    """
        Delete a file
    """
    def unlink(self, path):
        file_or_link = self.mongo.get_generic_file(filepath=path)
        self.mongo.remove_generic_file(generic_file=file_or_link)

    """
        Create a directory, no need to return anything
    """
    def mkdir(self, path, mode):
        GenericFile.new_generic_file(filepath=path, mode=mode, file_type=GenericFile.DIRECTORY_TYPE)

    """
        Delete a directory
    """
    def rmdir(self, path):
        directory = self.mongo.get_generic_file(filepath=path)
        self.mongo.remove_generic_file(generic_file=directory)

    """
        List files inside a directory
    """
    def readdir(self, path, fh):
        files = self.mongo.list_generic_files_in_directory(filepath=path)

        # We need to only keep final filename
        filenames = [file.filename for file in files]
        return ['.', '..'] + filenames

    """
        Write data to a file, from a specific offset. Returns the written data size
    """
    def write(self, path, data, offset, fh):
        file = self.mongo.get_generic_file(filepath=path)
        file.add_data(data=data, offset=offset)
        return len(data)

    """
        Truncate a file to a specific length
    """
    def truncate(self, path, length, fh=None):
        file = self.mongo.get_generic_file(filepath=path)
        file.truncate(length=length)

    """
        Return general information for a given path
    """
    def getattr(self, path, fh=None):
        gf = self.mongo.get_generic_file(filepath=path)
        if gf is None:
            raise FuseOSError(ENOENT)

        return gf.metadata

    """
        Set permissions to a given path
    """
    def chmod(self, path, mode):
        gf = self.mongo.get_generic_file(filepath=path)
        gf.metadata['st_mode'] &= 0o770000
        gf.metadata['st_mode'] |= mode
        gf.basic_save()
        return 0

    """
        Set owner (user & group) to a given path
    """
    def chown(self, path, uid, gid):
        gf = self.mongo.get_generic_file(filepath=path)
        gf.metadata['st_uid'] = uid
        gf.metadata['st_gid'] = gid
        gf.basic_save()

    """
        Return a specific special attribute for a given path (for selinux for example)
    """
    def getxattr(self, path, name, position=0):
        gf = self.mongo.get_generic_file(filepath=path)
        try:
            return gf.attrs[name]
        except KeyError:
            return ''  # Should return ENOATTR

    """
        Return all special attributes for a given path (for selinux for example)
    """
    def listxattr(self, path):
        gf = self.mongo.get_generic_file(filepath=path)
        return gf.attrs.keys()

    """
        Remove a specific special attribute for a given path
    """
    def removexattr(self, path, name):
        gf = self.mongo.get_generic_file(filepath=path)

        if name in gf.attrs:
            del gf.attrs[name]
            gf.basic_save()
        else:
            # Should return ENOATTR
            pass

    """
        Update the access and update time for a given path
    """
    def utimens(self, path, times=None):
        now = time.time()
        atime, mtime = times if times else (now, now)
        gf = self.mongo.get_generic_file(filepath=path)
        gf.metadata['st_atime'] = atime
        gf.metadata['st_mtime'] = mtime
        gf.basic_save()

    """
        Update a specific special attribute for a given path 
    """
    def setxattr(self, path, name, value, options, position=0):
        gf = self.mongo.get_generic_file(filepath=path)
        gf.attrs[name] = value
        gf.basic_save()

    """
        General (static) information about the current file system.
    """
    def statfs(self, path):
        print('Call statfs for '+str(path)+'...')
        return dict(f_bsize=65536*8, f_frsize=65536*8, f_blocks=65536*8, f_bavail=65536)

    """
        Flush data to MongoDB
    """
    def flush(self, path, fh):
        file = self.mongo.get_generic_file(filepath=path)
        self.mongo.flush_data_to_write(file=file)
        return None
class TestGenericFile(unittest.TestCase):
    def setUp(self):
        Configuration.FILEPATH = 'test/resources/conf/mongofs.json'
        self.mongo = Mongo(do_clean_up=True)
        GenericFile.mongo = self.mongo
        GenericFile.configuration = Configuration()
        self.utils = Utils(mongo=self.mongo)
        self.utils.load_files()

    def tearDown(self):
        self.mongo.clean_database()

    def test_basic_save(self):
        self.utils.insert_file()
        initial_gf = self.utils.files_coll.find_one(
            {
                'directory_id': self.utils.root_id,
                'filename': self.utils.file.filename
            }, {'uploadDate': False})

        self.utils.file.metadata['st_nlink'] = 37
        self.utils.file.basic_save()
        modified_gf = self.utils.files_coll.find_one(
            {
                'directory_id': self.utils.root_id,
                'filename': self.utils.file.filename
            }, {'uploadDate': False})

        self.assertEqual(modified_gf['metadata']['st_nlink'], 37)

    # We do not test is_dir / is_file / is_link here as we cannot load a GenericFile only, so the test would have no purpose.
    # We need to test those methods in test_File, test_Directory and test_SymbolicLink.

    def test_rename_to(self):
        self.utils.insert_file()
        initial_filename = self.utils.file.filename
        self.utils.file.rename_to(initial_filepath=self.utils.file.filepath,
                                  destination_filepath='/rename-file')
        old_file = self.utils.files_coll.find_one({
            'directory_id':
            self.utils.file.directory_id,
            'filename':
            initial_filename
        })
        self.assertEqual(old_file, None)

        new_file = self.utils.files_coll.find_one({
            'directory_id': self.utils.root_id,
            'filename': 'rename-file'
        })
        self.assertNotEqual(new_file, None)

    def test_release(self):
        self.utils.insert_file()
        gf = self.mongo.get_generic_file(filepath=self.utils.file.filepath,
                                         lock={'type': GenericFile.LOCK_WRITE})
        self.assertTrue('lock' in gf.json)
        gf.release(filepath=self.utils.file.filepath)

        gf = self.mongo.get_generic_file(filepath=self.utils.file.filepath)
        self.assertTrue('lock' not in gf.json or len(gf.json['lock']) == 0)

    def test_new_generic_file_file(self):
        # Creation of a basic file
        GenericFile.new_generic_file(filepath=self.utils.file.filepath,
                                     mode=0o755,
                                     file_type=GenericFile.FILE_TYPE)
        inserted_file = self.utils.files_coll.find_one({
            'directory_id':
            self.utils.file.directory_id,
            'filename':
            self.utils.file.filename
        })
        self.assertEqual(inserted_file['filename'], self.utils.file.filename)
        self.assertEqual(inserted_file['metadata']['st_mode'],
                         (S_IFREG | 0o755))
        self.assertEqual(inserted_file['generic_file_type'],
                         GenericFile.FILE_TYPE)

    def test_new_generic_file_directory(self):
        # Creation of a directory
        GenericFile.new_generic_file(filepath=self.utils.directory.filepath,
                                     mode=0o755,
                                     file_type=GenericFile.DIRECTORY_TYPE)
        inserted_file = self.utils.files_coll.find_one({
            'directory_id':
            self.utils.directory.directory_id,
            'filename':
            self.utils.directory.filename
        })
        self.assertEqual(inserted_file['filename'],
                         self.utils.directory.filename)
        self.assertEqual(inserted_file['metadata']['st_mode'],
                         (S_IFDIR | 0o755))
        self.assertEqual(inserted_file['generic_file_type'],
                         GenericFile.DIRECTORY_TYPE)

    def test_new_generic_file_symbolic_link(self):
        # Creation of a symbolic link to the initial self.utils.file just below
        self.utils.insert_file()

        GenericFile.new_generic_file(
            filepath=self.utils.symbolic_link.filepath,
            mode=0o755,
            file_type=GenericFile.SYMBOLIC_LINK_TYPE,
            target=self.utils.file.filename)
        inserted_file = self.utils.files_coll.find_one({
            'directory_id':
            self.utils.symbolic_link.directory_id,
            'filename':
            self.utils.symbolic_link.filename
        })
        self.assertEqual(inserted_file['filename'],
                         self.utils.symbolic_link.filename)
        self.assertEqual(inserted_file['metadata']['st_mode'],
                         (S_IFLNK | 0o755))
        self.assertEqual(inserted_file['generic_file_type'],
                         GenericFile.SYMBOLIC_LINK_TYPE)
        self.assertEqual(inserted_file['target'], self.utils.file.filename)

    def test_get_directory_id(self):
        self.utils.insert_directory()
        self.utils.insert_directory_file()

        directory_id = GenericFile.get_directory_id(
            filepath=self.utils.directory_file.filepath)
        self.assertEqual(directory_id, self.utils.directory_file.directory_id)

    def test_is_generic_filename_available(self):
        self.utils.insert_file()

        is_available = GenericFile.is_generic_filepath_available(
            filepath=self.utils.file.filepath)
        self.assertFalse(is_available)

        is_available = GenericFile.is_generic_filepath_available(
            filepath=self.utils.file.filepath + '.something')
        self.assertTrue(is_available)
Beispiel #9
0
class MongoFS(LoggingMixIn, Operations):
    # This is useful to be able to umount if there is an error to access MongoDB for example
    mounting_point = None

    def __init__(self):
        self.configuration = Configuration()
        self.mongo = Mongo()
        self.files = {}

        # Additional setup
        GenericFile.mongo = self.mongo
        GenericFile.configuration = self.configuration

        if self.configuration.is_development():
            # START DEBUG ONLY - Drop the old information from MongoDB
            self.mongo.clean_database()
            self.mongo = Mongo(
            )  # Create a new instance to be sure to have the top folder
            # END DEBUG ONLY

        # The top folder of the FS is automatically created by the Mongo class.

    """
        Create a file and returns a "file descriptor", which is in fact, simply the _id.
    """

    def create(self, path, mode, fi=None):
        file = GenericFile.new_generic_file(filepath=path,
                                            mode=mode,
                                            file_type=GenericFile.FILE_TYPE)
        return file.file_descriptor

    """
        Acquire a lock on a specific file.
    """

    def lock(self, path, fh, cmd, lock):
        # lock is a pointer to a struct flock. The first field is a short
        # with the lock type, so we can just cast it to a POINTER(c_short)
        # We won't manage locking only part of file, so we don't care about the
        # complete struct
        lock_type_pointer = cast(lock, POINTER(c_short))
        lock_type = lock_type_pointer[0]

        if cmd == fcntl.F_GETLK:
            blocking = self.mongo.test_lock_and_get_first_blocking(
                filepath=path, lock={'type': lock_type})
            if blocking is None:
                # We modify the pointer by setting F_UNLCK
                lock_type_pointer[0] = fcntl.F_UNLCK
            else:
                # We modify the pointer by setting the blocking type of the locks
                lock_type_pointer[0] = blocking['type']
        elif cmd == fcntl.F_SETLK or cmd == fcntl.F_SETLKW:
            if self.mongo.get_generic_file(filepath=path,
                                           lock={
                                               'type': lock_type,
                                               'wait': cmd == fcntl.F_SETLKW
                                           }) is None:
                raise FuseOSError(errno.ENOENT)
        else:
            raise FuseOSError(errno.EBADF)
        return 0

    """
        Release the lock on a specific file.
    """

    def release(self, path, fh):
        gf = self.mongo.get_generic_file(filepath=path)
        gf.release(filepath=path)
        return 0

    """
        Release the lock on a specific directory.
    """

    def releasedir(self, path, fh):
        gf = self.mongo.get_generic_file(filepath=path)
        gf.release(filepath=path)
        return 0

    """
        Rename a generic file
    """

    def rename(self, old, new):
        generic_file = self.mongo.get_generic_file(filepath=old)
        generic_file.rename_to(initial_filepath=old, destination_filepath=new)

    """
        Create a symbolic link to a generic file (source -> target). No need to return a file descriptor.
         target: the file we want to display if we display "source"
         source: the symbolic link itself
        Important: It seems we receive the symlink parameters in a different order than "ln -s TARGET SYMLINK",
        we receive them (SYMLINK, TARGET) which is kinda weird.
    """

    def symlink(self, source, target):
        GenericFile.new_generic_file(filepath=source,
                                     mode=0o777,
                                     file_type=GenericFile.SYMBOLIC_LINK_TYPE,
                                     target=target)

    """
        Read a symbolic link and return the file we should be redirected to.
    """

    def readlink(self, path):
        link = self.mongo.get_generic_file(filepath=path)
        return link.get_target()

    """
        Read a part of a file
    """

    def read(self, path, size, offset, fh):
        file = self.mongo.get_generic_file(filepath=path)
        tmp = file.read_data(offset=offset, size=size)
        return tmp

    """
        Delete a file
    """

    def unlink(self, path):
        file_or_link = self.mongo.get_generic_file(filepath=path)
        self.mongo.remove_generic_file(generic_file=file_or_link)

    """
        Create a directory, no need to return anything
    """

    def mkdir(self, path, mode):
        GenericFile.new_generic_file(filepath=path,
                                     mode=mode,
                                     file_type=GenericFile.DIRECTORY_TYPE)

    """
        Delete a directory
    """

    def rmdir(self, path):
        directory = self.mongo.get_generic_file(filepath=path)
        self.mongo.remove_generic_file(generic_file=directory)

    """
        List files inside a directory
    """

    def readdir(self, path, fh):
        files = self.mongo.list_generic_files_in_directory(filepath=path)

        # We need to only keep final filename
        filenames = [file.filename for file in files]
        return ['.', '..'] + filenames

    """
        Write data to a file, from a specific offset. Returns the written data size
    """

    def write(self, path, data, offset, fh):
        file = self.mongo.get_generic_file(filepath=path)
        file.add_data(data=data, offset=offset)
        return len(data)

    """
        Truncate a file to a specific length
    """

    def truncate(self, path, length, fh=None):
        file = self.mongo.get_generic_file(filepath=path)
        file.truncate(length=length)

    """
        Return general information for a given path
    """

    def getattr(self, path, fh=None):
        gf = self.mongo.get_generic_file(filepath=path)
        if gf is None:
            raise FuseOSError(errno.ENOENT)

        metadata = gf.metadata
        if gf.host != self.configuration.hostname():
            if metadata['st_uid'] != 0:
                uid = self.mongo.get_userid(gf.uname)
                if uid is not None:
                    metadata['st_uid'] = uid

            if metadata['st_gid'] != 0:
                gid = self.mongo.get_groupid(gf.gname)
                if gid is not None:
                    metadata['st_gid'] = gid
        return metadata

    """
        Set permissions to a given path
    """

    def chmod(self, path, mode):
        if path == '/' and configuration.force_root_mode():
            raise FuseOSError(errno.EACCESS)

        gf = self.mongo.get_generic_file(filepath=path)
        gf.metadata['st_mode'] &= 0o770000
        gf.metadata['st_mode'] |= mode
        gf.basic_save()
        return 0

    """
        Set owner (user & group) to a given path
    """

    def chown(self, path, uid, gid):
        gf = self.mongo.get_generic_file(filepath=path)
        gf.host = self.configuration.hostname()
        gf.metadata['st_uid'] = uid
        gf.metadata['st_gid'] = gid
        gf.uname = self.mongo.get_username(uid)
        gf.gname = self.mongo.get_groupname(gid)
        gf.basic_save()

    """
        Return a specific special attribute for a given path (for selinux for example)
    """

    def getxattr(self, path, name, position=0):
        gf = self.mongo.get_generic_file(filepath=path)
        try:
            return gf.attrs[name]
        except KeyError:
            return ''  # Should return ENOATTR

    """
        Return all special attributes for a given path (for selinux for example)
    """

    def listxattr(self, path):
        gf = self.mongo.get_generic_file(filepath=path)
        return gf.attrs.keys()

    """
        Remove a specific special attribute for a given path
    """

    def removexattr(self, path, name):
        gf = self.mongo.get_generic_file(filepath=path)

        if name in gf.attrs:
            del gf.attrs[name]
            gf.basic_save()
        else:
            raise FuseOSError(errno.EACCES)

    """
        Update the access and update time for a given path
    """

    def utimens(self, path, times=None):
        now = time.time()
        atime, mtime = times if times else (now, now)
        gf = self.mongo.get_generic_file(filepath=path)
        gf.metadata['st_atime'] = atime
        gf.metadata['st_mtime'] = mtime
        gf.basic_save()

    """
        Update a specific special attribute for a given path 
    """

    def setxattr(self, path, name, value, options, position=0):
        gf = self.mongo.get_generic_file(filepath=path)
        gf.attrs[name] = value
        gf.basic_save()

    """
        General (static) information about the current file system.
    """

    def statfs(self, path):
        print('Call statfs for ' + str(path) + '...')
        return dict(f_bsize=65536 * 8,
                    f_frsize=65536 * 8,
                    f_blocks=65536 * 8,
                    f_bavail=65536)

    """
        Flush data to MongoDB
    """

    def flush(self, path, fh):
        file = self.mongo.get_generic_file(filepath=path)
        self.mongo.flush_data_to_write(file=file)
        return None
Beispiel #10
0
class TestGenericFile(unittest.TestCase):
    def setUp(self):
        Configuration.FILEPATH = 'test/resources/conf/mongofs.json'
        self.mongo = Mongo()
        GenericFile.mongo = self.mongo
        GenericFile.configuration = Configuration()
        self.utils = Utils(mongo=self.mongo)
        self.utils.load_files()

    def tearDown(self):
        self.mongo.clean_database()

    # The tests below are mostly taken from test_Mongo as we need to check the same thing... A few changes have been
    # made to be sure to the test the right interface (GenericFile and not Mongo)
    def test_add_data_append(self):
        self.utils.insert_file()
        self.utils.insert_file_chunks()
        message = self.utils.read_file_chunks()

        self.utils.file.add_data(data=b'test', offset=len(message))
        modified_message = self.utils.read_file_chunks()
        self.assertEqual(modified_message, message + b'test')

    def test_add_data_replace(self):
        self.utils.insert_file()
        self.utils.insert_file_chunks()
        message = self.utils.read_file_chunks()
        message = list(message)
        message[2] = ord('t')
        message[3] = ord('h')
        message[4] = ord('i')
        message[5] = ord('n')
        message[6] = ord('g')
        message[7] = ord('s')
        expected_message = ''.join(map(chr, message))

        self.utils.file.add_data(data=b'things', offset=2)
        modified_message = self.utils.read_file_chunks()
        formatted_modified_message = ''.join(map(chr, list(modified_message)))
        self.assertEqual(formatted_modified_message, expected_message)

    def test_read_data(self):
        self.utils.insert_file()
        self.utils.insert_file_chunks()
        message = self.utils.read_file_chunks()

        data = self.utils.file.read_data(offset=0, size=4096)
        self.assertEqual(data, message)

        data = self.utils.file.read_data(offset=3, size=4096)
        self.assertEqual(data, message[3:])

        data = self.utils.file.read_data(offset=0, size=8)
        self.assertEqual(data, message[:8])

        data = self.utils.file.read_data(offset=3, size=8)
        self.assertEqual(data, message[3:3 + 8])

    def test_truncate(self):
        self.utils.insert_file()
        self.utils.insert_file_chunks()
        message = self.utils.read_file_chunks()

        self.utils.file.truncate(length=6)
        modified_message = self.utils.read_file_chunks()
        self.assertEqual(modified_message, message[0:6])

    def test_truncate_zero(self):
        self.utils.insert_file()
        self.utils.insert_file_chunks()
        message = self.utils.read_file_chunks()

        self.utils.file.truncate(length=0)
        modified_message = self.utils.read_file_chunks()
        self.assertEqual(modified_message, message[0:0])

    def test_is_file(self):
        self.assertTrue(self.utils.file.is_file())
        self.assertFalse(self.utils.directory.is_file())
        self.assertFalse(self.utils.symbolic_link.is_file())
        self.assertTrue(self.utils.directory_file.is_file())

    def test_is_dir(self):
        self.assertFalse(self.utils.file.is_dir())

    def test_is_link(self):
        self.assertFalse(self.utils.file.is_link())
Beispiel #11
0
 def setUpConfig(self, path):
     Configuration.FILEPATH = path
     GenericFile.configuration = Configuration()
     self.obj = Mongo(do_clean_up=True)
     GenericFile.mongo = self.obj