view mercurial/cffi/osutil.py @ 39278:53e532007878

cmdutil: return a revlog from openrevlog() and split function The filelog class is a wrapper around a revlog instance. I have plans to give manifests and the changelog a similar treatment. When filelog was ported away from revlog and when I started writing patches to do the same for manifests, I noticed that a lot of debug* and perf* commands were relying on low-level revlog APIs like start(), end(), deltaparent(), etc. For filelog, I added these to the interface, even though I didn't want to because they don't belong on a generic storage interface. For manifest (and eventually changelog), the pain is too much to bear. We need to cut the tight coupling. These debug* and perf* commands use cmdutil.openrevlog() to obtain a revlog instance. This commit effectively renames openrevlog() to openstorage(), adds an argument to ensure a revlog instance is returned, and introduces a replacement openrevlog() that calls openstorage() such that a revlog instance is returned. By doing things this way, we allow the debug* and perf* commands to still work on revlog-based repositories without having to expose low-level revlog APIs in the storage interfaces. The practical side-effect of this on the current code base is we return a revlog instance instead of a filelog. The manifest and changelog are not affected at this time. Some of filelog's storage APIs are different from revlog. For example, read() strips the optional header containing copy/rename metadata. This may impact some perf* commands. But I don't think the impact is worth worrying about. Upcoming commits will port existing consumers to openstorage(), where appropriate. This commit does cause some test regressions when using the simple store. These will be fixed as commands are ported to use storage APIs. .. api:: cmdutil.openrevlog() now returns a revlog instance or aborts Previously, it would return a storage object, which may not be a revlog instance. Use the new cmdutil.openstorage() API to return an object conforming to the storage interface of the thing you are accessing if you don't need a revlog instance. Differential Revision: https://phab.mercurial-scm.org/D4354
author Gregory Szorc <gregory.szorc@gmail.com>
date Tue, 14 Aug 2018 16:28:21 +0000
parents dacfcdd8b94e
children 2372284d9457
line wrap: on
line source

# osutil.py - CFFI version of osutil.c
#
# Copyright 2016 Maciej Fijalkowski <fijall@gmail.com>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.

from __future__ import absolute_import

import os
import stat as statmod

from ..pure.osutil import *

from .. import (
    pycompat,
)

if pycompat.isdarwin:
    from . import _osutil

    ffi = _osutil.ffi
    lib = _osutil.lib

    listdir_batch_size = 4096
    # tweakable number, only affects performance, which chunks
    # of bytes do we get back from getattrlistbulk

    attrkinds = [None] * 20 # we need the max no for enum VXXX, 20 is plenty

    attrkinds[lib.VREG] = statmod.S_IFREG
    attrkinds[lib.VDIR] = statmod.S_IFDIR
    attrkinds[lib.VLNK] = statmod.S_IFLNK
    attrkinds[lib.VBLK] = statmod.S_IFBLK
    attrkinds[lib.VCHR] = statmod.S_IFCHR
    attrkinds[lib.VFIFO] = statmod.S_IFIFO
    attrkinds[lib.VSOCK] = statmod.S_IFSOCK

    class stat_res(object):
        def __init__(self, st_mode, st_mtime, st_size):
            self.st_mode = st_mode
            self.st_mtime = st_mtime
            self.st_size = st_size

    tv_sec_ofs = ffi.offsetof("struct timespec", "tv_sec")
    buf = ffi.new("char[]", listdir_batch_size)

    def listdirinternal(dfd, req, stat, skip):
        ret = []
        while True:
            r = lib.getattrlistbulk(dfd, req, buf, listdir_batch_size, 0)
            if r == 0:
                break
            if r == -1:
                raise OSError(ffi.errno, os.strerror(ffi.errno))
            cur = ffi.cast("val_attrs_t*", buf)
            for i in range(r):
                lgt = cur.length
                assert lgt == ffi.cast('uint32_t*', cur)[0]
                ofs = cur.name_info.attr_dataoffset
                str_lgt = cur.name_info.attr_length
                base_ofs = ffi.offsetof('val_attrs_t', 'name_info')
                name = str(ffi.buffer(ffi.cast("char*", cur) + base_ofs + ofs,
                           str_lgt - 1))
                tp = attrkinds[cur.obj_type]
                if name == "." or name == "..":
                    continue
                if skip == name and tp == statmod.S_ISDIR:
                    return []
                if stat:
                    mtime = cur.mtime.tv_sec
                    mode = (cur.accessmask & ~lib.S_IFMT)| tp
                    ret.append((name, tp, stat_res(st_mode=mode, st_mtime=mtime,
                                st_size=cur.datalength)))
                else:
                    ret.append((name, tp))
                cur = ffi.cast("val_attrs_t*", int(ffi.cast("intptr_t", cur))
                    + lgt)
        return ret

    def listdir(path, stat=False, skip=None):
        req = ffi.new("struct attrlist*")
        req.bitmapcount = lib.ATTR_BIT_MAP_COUNT
        req.commonattr = (lib.ATTR_CMN_RETURNED_ATTRS |
                          lib.ATTR_CMN_NAME |
                          lib.ATTR_CMN_OBJTYPE |
                          lib.ATTR_CMN_ACCESSMASK |
                          lib.ATTR_CMN_MODTIME)
        req.fileattr = lib.ATTR_FILE_DATALENGTH
        dfd = lib.open(path, lib.O_RDONLY, 0)
        if dfd == -1:
            raise OSError(ffi.errno, os.strerror(ffi.errno))

        try:
            ret = listdirinternal(dfd, req, stat, skip)
        finally:
            try:
                lib.close(dfd)
            except BaseException:
                pass # we ignore all the errors from closing, not
                # much we can do about that
        return ret