From 10b86782721c3a5ca5850344404ed973f317a064 Mon Sep 17 00:00:00 2001
From: Michael Hanselmann <hansmi@google.com>
Date: Tue, 6 Nov 2012 18:27:39 +0100
Subject: [PATCH] Add utility to check if file is executable
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This replaces direct calls to β€œos.access” and
β€œos.path.exists”/β€œos.path.isfile”.

Signed-off-by: Michael Hanselmann <hansmi@google.com>
Reviewed-by: Guido Trotter <ultrotter@google.com>
Reviewed-by: Iustin Pop <iustin@google.com>
---
 lib/backend.py                        |  2 +-
 lib/utils/process.py                  |  4 +--
 lib/utils/wrapper.py                  | 11 +++++++
 test/ganeti.utils.wrapper_unittest.py | 46 +++++++++++++++++++++++++++
 4 files changed, 60 insertions(+), 3 deletions(-)

diff --git a/lib/backend.py b/lib/backend.py
index 0a3f2dc89..4aa851fff 100644
--- a/lib/backend.py
+++ b/lib/backend.py
@@ -711,7 +711,7 @@ def VerifyNode(what, cluster_name):
   if constants.NV_USERSCRIPTS in what:
     result[constants.NV_USERSCRIPTS] = \
       [script for script in what[constants.NV_USERSCRIPTS]
-       if not (os.path.exists(script) and os.access(script, os.X_OK))]
+       if not utils.IsExecutable(script)]
 
   if constants.NV_OOB_PATHS in what:
     result[constants.NV_OOB_PATHS] = tmp = []
diff --git a/lib/utils/process.py b/lib/utils/process.py
index e56932f9c..243151f32 100644
--- a/lib/utils/process.py
+++ b/lib/utils/process.py
@@ -718,8 +718,8 @@ def RunParts(dir_name, env=None, reset_env=False):
 
   for relname in sorted(dir_contents):
     fname = utils_io.PathJoin(dir_name, relname)
-    if not (os.path.isfile(fname) and os.access(fname, os.X_OK) and
-            constants.EXT_PLUGIN_MASK.match(relname) is not None):
+    if not (constants.EXT_PLUGIN_MASK.match(relname) is not None and
+            utils_wrapper.IsExecutable(fname)):
       rr.append((relname, constants.RUNPARTS_SKIP, None))
     else:
       try:
diff --git a/lib/utils/wrapper.py b/lib/utils/wrapper.py
index 982971e71..3b6f6f3dd 100644
--- a/lib/utils/wrapper.py
+++ b/lib/utils/wrapper.py
@@ -172,6 +172,17 @@ def GetClosedTempfile(*args, **kwargs):
   return path
 
 
+def IsExecutable(filename):
+  """Checks whether a file exists and is executable.
+
+  @type filename: string
+  @param filename: Filename
+  @rtype: bool
+
+  """
+  return os.path.isfile(filename) and os.access(filename, os.X_OK)
+
+
 def ResetTempfileModule(_time=time.time):
   """Resets the random name generator of the tempfile module.
 
diff --git a/test/ganeti.utils.wrapper_unittest.py b/test/ganeti.utils.wrapper_unittest.py
index 16633f7a2..cfdae9a15 100755
--- a/test/ganeti.utils.wrapper_unittest.py
+++ b/test/ganeti.utils.wrapper_unittest.py
@@ -27,6 +27,7 @@ import os
 import socket
 import tempfile
 import unittest
+import shutil
 
 from ganeti import constants
 from ganeti import utils
@@ -122,5 +123,50 @@ class TestIgnoreSignals(unittest.TestCase):
     self.assertEquals(utils.IgnoreSignals(self._Return, 33), 33)
 
 
+class TestIsExecutable(unittest.TestCase):
+  def setUp(self):
+    self.tmpdir = tempfile.mkdtemp()
+
+  def tearDown(self):
+    shutil.rmtree(self.tmpdir)
+
+  def testNonExisting(self):
+    fname = utils.PathJoin(self.tmpdir, "file")
+    assert not os.path.exists(fname)
+    self.assertFalse(utils.IsExecutable(fname))
+
+  def testNoFile(self):
+    path = utils.PathJoin(self.tmpdir, "something")
+    os.mkdir(path)
+    assert os.path.isdir(path)
+    self.assertFalse(utils.IsExecutable(path))
+
+  def testExecutable(self):
+    fname = utils.PathJoin(self.tmpdir, "file")
+    utils.WriteFile(fname, data="#!/bin/bash", mode=0700)
+    assert os.path.exists(fname)
+    self.assertTrue(utils.IsExecutable(fname))
+
+    self.assertTrue(self._TestSymlink(fname))
+
+  def testFileNotExecutable(self):
+    fname = utils.PathJoin(self.tmpdir, "file")
+    utils.WriteFile(fname, data="#!/bin/bash", mode=0600)
+    assert os.path.exists(fname)
+    self.assertFalse(utils.IsExecutable(fname))
+
+    self.assertFalse(self._TestSymlink(fname))
+
+  def _TestSymlink(self, fname):
+    assert os.path.exists(fname)
+
+    linkname = utils.PathJoin(self.tmpdir, "cmd")
+    os.symlink(fname, linkname)
+
+    assert os.path.islink(linkname)
+
+    return utils.IsExecutable(linkname)
+
+
 if __name__ == "__main__":
   testutils.GanetiTestProgram()
-- 
GitLab