Description: Fix the sandbox, by implementing os.access
 pypy now (since b97c7d6f7fd8) checks for executability, when searching for its
 own executable on startup. This requires os.access.
 Abuse "kind" to maintain extra mode bits, so we can store the executable state
 in our vfs.
Author: Stefano Rivera <stefanor@debian.org>
Origin: upstream, https://bitbucket.org/pypy/pypy/commits/17fcf0cc640db450f7ee5709e4fb9550768768a4 https://bitbucket.org/pypy/pypy/commits/1599b45fa4e120b1595059b2c7db34427ba2a9ae
Last-Update: 2014-09-22

--- a/rpython/translator/sandbox/sandlib.py
+++ b/rpython/translator/sandbox/sandlib.py
@@ -459,6 +459,15 @@
 
     do_ll_os__ll_os_lstat = do_ll_os__ll_os_stat
 
+    def do_ll_os__ll_os_access(self, vpathname, mode):
+        try:
+            node = self.get_node(vpathname)
+        except OSError, e:
+            if e.errno == errno.ENOENT:
+                return False
+            raise
+        return node.access(mode)
+
     def do_ll_os__ll_os_isatty(self, fd):
         return self.virtual_console_isatty and fd in (0, 1, 2)
 
--- a/rpython/translator/sandbox/test/test_vfs.py
+++ b/rpython/translator/sandbox/test/test_vfs.py
@@ -33,6 +33,8 @@
     py.test.raises(OSError, d.join, 'bar')
     st = d.stat()
     assert stat.S_ISDIR(st.st_mode)
+    assert d.access(os.R_OK | os.X_OK)
+    assert not d.access(os.W_OK)
 
 def test_file():
     f = File('hello world')
@@ -46,6 +48,8 @@
     st = f.stat()
     assert stat.S_ISREG(st.st_mode)
     assert st.st_size == 11
+    assert f.access(os.R_OK)
+    assert not f.access(os.W_OK)
 
 def test_realdir_realfile():
     for show_dotfiles in [False, True]:
@@ -78,6 +82,7 @@
 
                 f = v_test_vfs.join('symlink2')
                 assert stat.S_ISREG(f.stat().st_mode)
+                assert f.access(os.R_OK)
                 assert f.open().read() == 'secret'
             else:
                 py.test.raises(OSError, v_test_vfs.join, 'symlink1')
--- a/rpython/translator/sandbox/vfs.py
+++ b/rpython/translator/sandbox/vfs.py
@@ -22,7 +22,7 @@
         st_size = self.getsize()
         st_mode = self.kind
         st_mode |= stat.S_IWUSR | stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH
-        if self.kind == stat.S_IFDIR:
+        if stat.S_ISDIR(self.kind):
             st_mode |= stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH
         if self.read_only:
             st_uid = 0       # read-only files are virtually owned by root
@@ -37,6 +37,15 @@
             (st_mode, st_ino, st_dev, st_nlink, st_uid, st_gid,
              st_size, st_atime, st_mtime, st_ctime))
 
+    def access(self, mode):
+        s = self.stat()
+        e_mode = s.st_mode & stat.S_IRWXO
+        if UID == s.st_uid:
+            e_mode |= (s.st_mode & stat.S_IRWXU) >> 6
+        if GID == s.st_gid:
+            e_mode |= (s.st_mode & stat.S_IRWXG) >> 3
+        return (e_mode & mode) == mode
+
     def keys(self):
         raise OSError(errno.ENOTDIR, self)
 
@@ -114,8 +123,9 @@
         return cStringIO.StringIO(self.data)
 
 class RealFile(File):
-    def __init__(self, path):
+    def __init__(self, path, mode=0):
         self.path = path
+        self.kind |= mode
     def __repr__(self):
         return '<RealFile %s>' % (self.path,)
     def getsize(self):
--- a/pypy/sandbox/pypy_interact.py
+++ b/pypy/sandbox/pypy_interact.py
@@ -55,7 +55,7 @@
 
         return Dir({
             'bin': Dir({
-                'pypy-c': RealFile(self.executable),
+                'pypy-c': RealFile(self.executable, mode=0111),
                 'lib-python': RealDir(os.path.join(libroot, 'lib-python'),
                                       exclude=exclude), 
                 'lib_pypy': RealDir(os.path.join(libroot, 'lib_pypy'),
