Mercurial > hg
view mercurial/cffi/osutil.py @ 37047:fddcb51b5084
wireproto: define permissions-based routing of HTTPv2 wire protocol
Now that we have a scaffolding for serving version 2 of the HTTP
protocol, let's start implementing it.
A good place to start is URL routing and basic request processing
semantics. We can focus on content types, capabilities detect, etc
later.
Version 2 of the HTTP wire protocol encodes the needed permissions
of the request in the URL path. The reasons for this are documented
in the added documentation. In short, a) it makes it really easy and
fail proof for server administrators to implement path-based
authentication and b) it will enable clients to realize very early in
a server exchange that authentication will be required to complete
the operation. This latter point avoids all kinds of complexity and
problems, like dealing with Expect: 100-continue and clients finding
out later during `hg push` that they need to provide authentication.
This will avoid the current badness where clients send a full bundle,
get an HTTP 403, provide authentication, then retransmit the bundle.
In order to implement command checking, we needed to implement a
protocol handler for the new wire protocol. Our handler is just
small enough to run the code we've implemented.
Tests for the defined functionality have been added.
I very much want to refactor the permissions checking code and define
a better response format. But this can be done later. Nothing is
covered by backwards compatibility at this point.
Differential Revision: https://phab.mercurial-scm.org/D2836
author | Gregory Szorc <gregory.szorc@gmail.com> |
---|---|
date | Mon, 19 Mar 2018 16:43:47 -0700 |
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