Example #1
0
 def getxattr(self, path, *args, **kwargs):
     if path in self._intermediates:
         raise fuse.FuseOSError(errno.ENODATA)
     try:
         (path, fs) = self._map_path(path)
     except ValueError:
         raise fuse.FuseOSError(errno.ENOENT)
     return getattr(fs, 'getxattr')(path, *args, **kwargs)
Example #2
0
 def getxattr(self, path, attrname):
     if path == '/' or path in self._write_buffers:
         raise fuse.FuseOSError(errno.ENODATA)
     (g, e) = self._path_to_guildmoji(path)
     if not g and e:
         raise fuse.FuseOSError(errno.ENOENT)
     if attrname == constants.URL_XATTR_NAME:
         return bytes(self._emoji_url(e), 'utf-8')
     elif attrname == constants.CREATEDBY_XATTR_NAME:
         return bytes(self._user_string(e['user']), 'utf-8')
     else:
         raise fuse.FuseOSError(errno.ENODATA)
Example #3
0
 def create(self, path, mode, fi=None):
     g = self._path_to_guild(path)
     if not g:
         raise fuse.FuseOSError(errno.ENOENT)
     if not self._guild_is_writable(g):
         raise fuse.FuseOSError(errno.EPERM)
     try:
         self._path_to_extension(path)
     except ValueError:
         raise fuse.FuseOSError(errno.EINVAL)
     self._write_buffers[path] = io.BytesIO()
     return 0
Example #4
0
 def unlink(self, path):
     # TODO: what about an unlink on a new file open in _write_buffers ?
     (g, e) = self._path_to_guildmoji(path)
     if g and e:
         logger.info('🗑️ Deleting :%s: (id %s) from "%s" (id %s)',
                     e['name'], e['id'], g['name'], g['id'])
         self._request('DELETE', f"guilds/{g['id']}/emojis/{e['id']}")
         self._invalidate_guild(g['id'])
     elif g and not e:
         # Sorry, but we won't delete a whole discord.
         raise fuse.FuseOSError(errno.EPERM)
     else:
         raise fuse.FuseOSError(errno.ENOENT)
Example #5
0
 def getxattr(self, path, attrname):
     if path == '/' or path in self._write_buffers:
         raise fuse.FuseOSError(errno.ENODATA)
     emojis = self._get_all_emoji()
     name = self._path_to_name(path)
     e = emojis[name]
     if name not in emojis:
         raise fuse.FuseOSError(errno.ENOENT)
     if attrname == constants.URL_XATTR_NAME:
         return bytes(e['url'], 'utf-8')
     elif attrname == constants.CREATEDBY_XATTR_NAME:
         return bytes(e['user_display_name'], 'utf-8')
     else:
         raise fuse.FuseOSError(errno.ENODATA)
Example #6
0
 def readlink(self, path):
     emojis = self._get_all_emoji()
     name = self._path_to_name(path)
     if name not in emojis:
         raise fuse.FuseOSError(errno.ENOENT)
     e = emojis[name]
     if not e['is_alias']:
         raise fuse.FuseOSError(errno.EINVAL)
     # We don't 'deference' it ourselves using our metadata table, because it could be an alias
     # to a regular Unicode emoji.
     # This will represent it as a dangling symlink instead of throwing.
     # TODO: do something smart and/or reasonable about regular Unicode emoji
     #       (possibly, have another mountpoint for them?)
     return self._emoji_to_filename(e, name=e['alias_for'])
Example #7
0
 def unlink(self, path):
     # TODO: what about an unlink on a new file open in _write_buffers ?
     emojis = self._get_all_emoji()
     name = self._path_to_name(path)
     if name not in emojis:
         raise fuse.FuseOSError(errno.ENOENT)
     self._delete_emoji(name)
Example #8
0
    def _request(self, method, url, **kwargs):
        """Execute a request against our _session, respecting ratelimiting and raising if
        the response isn't okay.  Retry on ratelimiting (after sleeping) but not on other error."""
        # TODO: this is very close to, but not quite, Discord._request().

        # Respect any ratelimiting on the given URL path
        time.sleep(max(0, self._retry_after.get(url, 0) - time.time()))
        resp = self._session.request(method, url, **kwargs)
        if resp.status_code == 429:
            self._retry_after[url] = time.time() + resp.headers.get(
                'retry-after', 60)
            logger.warn(
                'Got ratelimited by Slack; retrying after %s seconds for %s',
                self._retry_after[url], url)
            return self._request(method, url, **kwargs)
        else:
            resp.raise_for_status()
        j = resp.json()
        logger.debug('resp for %s to %s json: %s', method, url, j)
        if not j['ok']:
            logger.error('Got an error on %s to %s: %s', method, url,
                         j['error'])
            if j['error'] == 'no_permission':
                raise fuse.FuseOSError(errno.EPERM)
        assert (j['ok'])
        return resp
Example #9
0
 def listxattr(self, path):
     if path == '/' or path in self._write_buffers:
         return []
     (g, e) = self._path_to_guildmoji(path)
     if not g and e:
         raise fuse.FuseOSError(errno.ENOENT)
     return [constants.URL_XATTR_NAME, constants.CREATEDBY_XATTR_NAME]
Example #10
0
 def symlink(self, target_path, source_path):
     emojis = self._get_all_emoji()
     source = self._path_to_name(source_path)
     if source not in emojis:
         raise fuse.FuseOSError(errno.ENOENT)
     target = self._path_to_name(target_path)
     self._alias_emoji(source, target)
Example #11
0
    def listxattr(self, path):
        if path == '/' or path in self._write_buffers:
            return []
        emojis = self._get_all_emoji()
        name = self._path_to_name(path)
        if name not in emojis:
            raise fuse.FuseOSError(errno.ENOENT)

        return [constants.URL_XATTR_NAME, constants.CREATEDBY_XATTR_NAME]
Example #12
0
    def read(self, path, size, offset, fh):
        if path in self._write_buffers:
            b = self._write_buffers[path]
            b.seek(offset)
            return b.read(size)

        (g, e) = self._path_to_guildmoji(path)
        if g and e:
            b = utils.get_emoji_bytes(self._emoji_url(e))
            return b[offset:offset + size]
        else:
            raise fuse.FuseOSError(errno.ENOENT)
Example #13
0
    def read(self, path, size, offset, fh):
        if path in self._write_buffers:
            b = self._write_buffers[path]
            b.seek(offset)
            return b.read(size)

        emojis = self._get_all_emoji()
        name = self._path_to_name(path)
        if name not in emojis:
            raise fuse.FuseOSError(errno.ENOENT)
        e = emojis[name]
        b = utils.get_emoji_bytes(e['url'])
        return b[offset:offset + size]
Example #14
0
 def getattr(self, path, *args, **kwargs):
     # Serve directory entries for our intermediates.
     if path in self._intermediates:
         return dict(
             st_mode=stat.S_IFDIR | 0o555,
             st_nlink=2,
             st_uid=utils.getuid(),
             st_gid=utils.getgid(),
         )
     # Delegate to mountpoints their / and below.
     try:
         (path, fs) = self._map_path(path)
     except ValueError:
         raise fuse.FuseOSError(errno.ENOENT)
     return getattr(fs, 'getattr')(path, *args, **kwargs)
Example #15
0
 def _path_to_guildmoji(self, path):
     '''Given a /discord/foo/bar.png path, find and return (guild, emoji) objects.'''
     g = self._path_to_guild(path)
     if g:
         eh = self._path_to_emojiname(path)
         if eh:
             emojis = self._get_emojis(g['id'])
             if eh in emojis:
                 return (g, emojis[eh])
             else:
                 raise fuse.FuseOSError(errno.ENOENT)
         else:
             return (g, None)
         # TODO: Better document (& test!) the semantics of this.
     else:
         return (None, None)
Example #16
0
    def getattr(self, path, fh):
        if path == '/':
            return dict(
                st_mode=stat.S_IFDIR | 0o555 | stat.S_IWUSR,
                st_mtime=time.time(),
                st_ctime=time.time(),
                st_atime=time.time(),
                st_nlink=2,
                st_uid=utils.getuid(),
                st_gid=utils.getgid(),
            )

        if path in self._write_buffers:
            return dict(st_mode=stat.S_IFREG | 0o600,
                        st_atime=time.time(),
                        st_ctime=time.time(),
                        st_mtime=time.time(),
                        st_nlink=1,
                        st_uid=utils.getuid(),
                        st_gid=utils.getgid(),
                        st_size=len(self._write_buffers[path].getbuffer()))

        # This will raise a Fuse ENOENT if a nonexistent emoji name was specified in the path
        (g, e) = self._path_to_guildmoji(path)
        if g is None:
            raise fuse.FuseOSError(errno.ENOENT)
        elif e is None:
            return dict(
                st_mode=stat.S_IFDIR | 0o555 |
                (stat.S_IWUSR if self._guild_is_writable(g) else 0),
                st_mtime=time.time(),
                st_ctime=time.time(),
                st_atime=time.time(),
                st_nlink=2,
                st_uid=utils.getuid(),
                st_gid=utils.getgid(),
            )
        else:
            return dict(st_mode=stat.S_IFREG | 0o444,
                        st_atime=time.time(),
                        st_ctime=time.time(),
                        st_mtime=time.time(),
                        st_nlink=1,
                        st_uid=utils.getuid(),
                        st_gid=utils.getgid(),
                        st_size=utils.get_content_length(self._emoji_url(e)))
Example #17
0
    def getattr(self, path, fh):
        emojis = self._get_all_emoji()

        # TODO: if we don't set allow_others in our fuse_main invocation,
        #       the 0o555 is probably just confusing.
        if path == '/':
            return dict(
                st_mode=stat.S_IFDIR | 0o555 | stat.S_IWUSR,
                st_mtime=max([e['created'] for e in emojis.values()]),
                st_ctime=min([e['created'] for e in emojis.values()]),
                st_atime=time.time(),
                st_nlink=2,
                st_uid=utils.getuid(),
                st_gid=utils.getgid(),
            )

        if path in self._write_buffers:
            return dict(st_mode=stat.S_IFREG | 0o600,
                        st_atime=time.time(),
                        st_ctime=time.time(),
                        st_mtime=time.time(),
                        st_nlink=1,
                        st_uid=utils.getuid(),
                        st_gid=utils.getgid(),
                        st_size=len(self._write_buffers[path].getbuffer()))

        name = self._path_to_name(path)
        if name not in emojis:
            raise fuse.FuseOSError(errno.ENOENT)

        e = emojis[name]

        return dict(
            st_mode=(stat.S_IFLNK if e['is_alias'] else stat.S_IFREG) | 0o444,
            st_mtime=e['created'],
            st_ctime=e['created'],
            st_atime=time.time(),
            st_nlink=1,
            st_uid=utils.getuid(),
            st_gid=utils.getgid(),
            st_size=(utils.get_content_length(e['url'])
                     if self._real_sizes else 256 * 1024),
        )