diff --git a/Makefile.am b/Makefile.am
index 4a4b9bf2a2ecf02255d4942719ee330ba7ddf606..78cefde16ace3118eff7d3d794a575495c7f5938 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -876,6 +876,7 @@ lib/_autoconf.py: Makefile vcs-version | lib/.dir
 	  echo "CONFD_USER = '$(CONFD_USER)'"; \
 	  echo "CONFD_GROUP = '$(CONFD_GROUP)'"; \
 	  echo "NODED_USER = '$(NODED_USER)'"; \
+	  echo "NODED_GROUP = '$(NODED_GROUP)'"; \
 	  echo "VCS_VERSION = '$$VCSVER'"; \
 	  echo "DISK_SEPARATOR = '$(DISK_SEPARATOR)'"; \
 	  if [ "$(HTOOLS)" ]; then \
diff --git a/configure.ac b/configure.ac
index 502456152a61bac6a5ae7c6f24a2d7012aac5bf5..8baf2c3142de00da31708a91e2be1e7f0d2c15f0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -183,16 +183,19 @@ AC_ARG_WITH([group-prefix],
    group_admin="${withval}admin";
    group_confd="${withval}confd";
    group_masterd="${withval}masterd";
+   group_noded="root";
    group_daemons="${withval}daemons";],
   [group_rapi="root";
    group_admin="root";
    group_confd="root";
    group_masterd="root";
+   group_noded="root";
    group_daemons="root"])
 AC_SUBST(RAPI_GROUP, $group_rapi)
 AC_SUBST(ADMIN_GROUP, $group_admin)
 AC_SUBST(CONFD_GROUP, $group_confd)
 AC_SUBST(MASTERD_GROUP, $group_masterd)
+AC_SUBST(NODED_GROUP, $group_noded)
 AC_SUBST(DAEMONS_GROUP, $group_daemons)
 
 # Print the config to the user
diff --git a/lib/backend.py b/lib/backend.py
index d6dd724b4fde3bea01edd82274ceb30210fe3c71..295cd12db413ea702e154f7a8ea9258c65252695 100644
--- a/lib/backend.py
+++ b/lib/backend.py
@@ -1850,10 +1850,10 @@ def UploadFile(file_name, data, mode, uid, gid, atime, mtime):
   @param data: the new contents of the file
   @type mode: int
   @param mode: the mode to give the file (can be None)
-  @type uid: int
-  @param uid: the owner of the file (can be -1 for default)
-  @type gid: int
-  @param gid: the group of the file (can be -1 for default)
+  @type uid: string
+  @param uid: the owner of the file
+  @type gid: string
+  @param gid: the group of the file
   @type atime: float
   @param atime: the atime to set on the file (can be None)
   @type mtime: float
@@ -1870,6 +1870,13 @@ def UploadFile(file_name, data, mode, uid, gid, atime, mtime):
 
   raw_data = _Decompress(data)
 
+  if not (isinstance(uid, basestring) and isinstance(gid, basestring)):
+    _Fail("Invalid username/groupname type")
+
+  getents = runtime.GetEnts()
+  uid = getents.LookupUser(uid)
+  gid = getents.LookupGroup(gid)
+
   utils.SafeWriteFile(file_name, None,
                       data=raw_data, mode=mode, uid=uid, gid=gid,
                       atime=atime, mtime=mtime)
diff --git a/lib/constants.py b/lib/constants.py
index 2a36acb60da042e2e546cc3446aa88e1d3c34a3e..a33186b008268c380fd8195b587d8b9aee1e3819 100644
--- a/lib/constants.py
+++ b/lib/constants.py
@@ -96,6 +96,7 @@ RAPI_GROUP = _autoconf.RAPI_GROUP
 CONFD_USER = _autoconf.CONFD_USER
 CONFD_GROUP = _autoconf.CONFD_GROUP
 NODED_USER = _autoconf.NODED_USER
+NODED_GROUP = _autoconf.NODED_GROUP
 
 
 # Wipe
diff --git a/lib/daemon.py b/lib/daemon.py
index 6d6bd743ddd9acde2e16c905a323be3dd8635911..974c8d87cf35f079814d6ba42dd3655b49b5268c 100644
--- a/lib/daemon.py
+++ b/lib/daemon.py
@@ -435,6 +435,9 @@ class Mainloop(object):
     self._signal_wait = []
     self.scheduler = AsyncoreScheduler(time.time)
 
+    # Resolve uid/gids used
+    runtime.GetEnts()
+
   @utils.SignalHandled([signal.SIGCHLD])
   @utils.SignalHandled([signal.SIGTERM])
   @utils.SignalHandled([signal.SIGINT])
@@ -449,6 +452,7 @@ class Mainloop(object):
            len(signal_handlers) > 0, \
            "Broken SignalHandled decorator"
     running = True
+
     # Start actual main loop
     while running:
       if not self.scheduler.empty():
diff --git a/lib/rpc.py b/lib/rpc.py
index ddaaad77aec94ab813e367fae8c528ee66e278b0..e482a20f3e667209893995b5d1d5988a5268043a 100644
--- a/lib/rpc.py
+++ b/lib/rpc.py
@@ -45,6 +45,7 @@ from ganeti import constants
 from ganeti import errors
 from ganeti import netutils
 from ganeti import ssconf
+from ganeti import runtime
 
 # pylint has a bug here, doesn't see this import
 import ganeti.http.client  # pylint: disable-msg=W0611
@@ -1177,8 +1178,9 @@ class RpcRunner(object):
     file_contents = utils.ReadFile(file_name)
     data = cls._Compress(file_contents)
     st = os.stat(file_name)
-    params = [file_name, data, st.st_mode, st.st_uid, st.st_gid,
-              st.st_atime, st.st_mtime]
+    getents = runtime.GetEnts()
+    params = [file_name, data, st.st_mode, getents.LookupUid(st.st_uid),
+              getents.LookupGid(st.st_gid), st.st_atime, st.st_mtime]
     return cls._StaticMultiNodeCall(node_list, "upload_file", params,
                                     address_list=address_list)
 
diff --git a/lib/runtime.py b/lib/runtime.py
index 2d65ef0baa185b3904638b59d0668bc191b59dc1..4d5e3efc624861e138c08e393c4c0febbd26f540 100644
--- a/lib/runtime.py
+++ b/lib/runtime.py
@@ -28,6 +28,7 @@ import threading
 
 from ganeti import constants
 from ganeti import errors
+from ganeti import utils
 
 
 _priv = None
@@ -91,11 +92,79 @@ class GetentResolver:
     self.rapi_gid = GetGid(constants.RAPI_GROUP, _getgrnam)
 
     self.noded_uid = GetUid(constants.NODED_USER, _getpwnam)
+    self.noded_gid = GetGid(constants.NODED_GROUP, _getgrnam)
 
     # Misc Ganeti groups
     self.daemons_gid = GetGid(constants.DAEMONS_GROUP, _getgrnam)
     self.admin_gid = GetGid(constants.ADMIN_GROUP, _getgrnam)
 
+    self._uid2user = {
+      self.masterd_uid: constants.MASTERD_USER,
+      self.confd_uid: constants.CONFD_USER,
+      self.rapi_uid: constants.RAPI_USER,
+      self.noded_uid: constants.NODED_USER,
+      }
+
+    self._gid2group = {
+      self.masterd_gid: constants.MASTERD_GROUP,
+      self.confd_gid: constants.CONFD_GROUP,
+      self.rapi_gid: constants.RAPI_GROUP,
+      self.noded_gid: constants.NODED_GROUP,
+      self.daemons_gid: constants.DAEMONS_GROUP,
+      self.admin_gid: constants.ADMIN_GROUP,
+      }
+
+    self._user2uid = utils.InvertDict(self._uid2user)
+    self._group2gid = utils.InvertDict(self._gid2group)
+
+  def LookupUid(self, uid):
+    """Looks which Ganeti user belongs to this uid.
+
+    @param uid: The uid to lookup
+    @returns The user name associated with that uid
+
+    """
+    try:
+      return self._uid2user[uid]
+    except KeyError:
+      raise errors.ConfigurationError("Unknown Ganeti uid '%d'" % uid)
+
+  def LookupGid(self, gid):
+    """Looks which Ganeti group belongs to this gid.
+
+    @param gid: The gid to lookup
+    @returns The group name associated with that gid
+
+    """
+    try:
+      return self._gid2group[gid]
+    except KeyError:
+      raise errors.ConfigurationError("Unknown Ganeti gid '%d'" % gid)
+
+  def LookupUser(self, name):
+    """Looks which uid belongs to this name.
+
+    @param name: The name to lookup
+    @returns The uid associated with that user name
+
+    """
+    try:
+      return self._user2uid[name]
+    except KeyError:
+      raise errors.ConfigurationError("Unknown Ganeti user '%s'" % name)
+
+  def LookupGroup(self, name):
+    """Looks which gid belongs to this name.
+
+    @param name: The name to lookup
+    @returns The gid associated with that group name
+
+    """
+    try:
+      return self._group2gid[name]
+    except KeyError:
+      raise errors.ConfigurationError("Unknown Ganeti group '%s'" % name)
+
 
 def GetEnts(resolver=GetentResolver):
   """Singleton wrapper around resolver instance.
diff --git a/lib/utils/algo.py b/lib/utils/algo.py
index 543ac776873e307243bca5310ea566a72084ccb7..280a1b736cb7324d4a2724d9afdedc4a2437260e 100644
--- a/lib/utils/algo.py
+++ b/lib/utils/algo.py
@@ -115,6 +115,16 @@ def NiceSort(values, key=None):
   return sorted(values, key=keyfunc)
 
 
+def InvertDict(dict_in):
+  """Inverts the key/value mapping of a dict.
+
+  @param dict_in: The dict to invert
+  @returns the inverted dict
+
+  """
+  return dict(zip(dict_in.values(), dict_in.keys()))
+
+
 class RunningTimeout(object):
   """Class to calculate remaining timeout when doing several operations.
 
diff --git a/test/ganeti.runtime_unittest.py b/test/ganeti.runtime_unittest.py
index 3ba9ac873b68b20f2c1d0c3bb26f7c01128d194d..79ede58bc71730c96af7208cbf82173cd3f4257e 100755
--- a/test/ganeti.runtime_unittest.py
+++ b/test/ganeti.runtime_unittest.py
@@ -25,6 +25,7 @@ from ganeti import errors
 from ganeti import runtime
 
 import testutils
+import unittest
 
 
 class _EntStub:
@@ -50,6 +51,7 @@ def _StubGetgrnam(group):
     constants.RAPI_GROUP: _EntStub(gid=2),
     constants.DAEMONS_GROUP: _EntStub(gid=3),
     constants.ADMIN_GROUP: _EntStub(gid=4),
+    constants.NODED_GROUP: _EntStub(gid=5),
     }
   return groups[group]
 
@@ -67,29 +69,30 @@ class ResolverStubRaising(object):
     raise errors.ConfigurationError("No entries")
 
 
-class TestErrors(testutils.GanetiTestCase):
-  def testEverythingSuccessful(self):
-    resolver = runtime.GetentResolver(_getpwnam=_StubGetpwnam,
-                                      _getgrnam=_StubGetgrnam)
+class TestErrors(unittest.TestCase):
+  def setUp(self):
+    self.resolver = runtime.GetentResolver(_getpwnam=_StubGetpwnam,
+                                           _getgrnam=_StubGetgrnam)
 
-    self.assertEqual(resolver.masterd_uid,
+  def testEverythingSuccessful(self):
+    self.assertEqual(self.resolver.masterd_uid,
                      _StubGetpwnam(constants.MASTERD_USER).pw_uid)
-    self.assertEqual(resolver.masterd_gid,
+    self.assertEqual(self.resolver.masterd_gid,
                      _StubGetgrnam(constants.MASTERD_GROUP).gr_gid)
-    self.assertEqual(resolver.confd_uid,
+    self.assertEqual(self.resolver.confd_uid,
                      _StubGetpwnam(constants.CONFD_USER).pw_uid)
-    self.assertEqual(resolver.confd_gid,
+    self.assertEqual(self.resolver.confd_gid,
                      _StubGetgrnam(constants.CONFD_GROUP).gr_gid)
-    self.assertEqual(resolver.rapi_uid,
+    self.assertEqual(self.resolver.rapi_uid,
                      _StubGetpwnam(constants.RAPI_USER).pw_uid)
-    self.assertEqual(resolver.rapi_gid,
+    self.assertEqual(self.resolver.rapi_gid,
                      _StubGetgrnam(constants.RAPI_GROUP).gr_gid)
-    self.assertEqual(resolver.noded_uid,
+    self.assertEqual(self.resolver.noded_uid,
                      _StubGetpwnam(constants.NODED_USER).pw_uid)
 
-    self.assertEqual(resolver.daemons_gid,
+    self.assertEqual(self.resolver.daemons_gid,
                      _StubGetgrnam(constants.DAEMONS_GROUP).gr_gid)
-    self.assertEqual(resolver.admin_gid,
+    self.assertEqual(self.resolver.admin_gid,
                      _StubGetgrnam(constants.ADMIN_GROUP).gr_gid)
 
   def testUserNotFound(self):
@@ -104,6 +107,36 @@ class TestErrors(testutils.GanetiTestCase):
     self.assertRaises(errors.ConfigurationError, runtime.GetEnts,
                       resolver=ResolverStubRaising)
 
+  def testLookupForUser(self):
+    master_stub = _StubGetpwnam(constants.MASTERD_USER)
+    rapi_stub = _StubGetpwnam(constants.RAPI_USER)
+    self.assertEqual(self.resolver.LookupUid(master_stub.pw_uid),
+                     constants.MASTERD_USER)
+    self.assertEqual(self.resolver.LookupUid(rapi_stub.pw_uid),
+                     constants.RAPI_USER)
+    self.assertEqual(self.resolver.LookupUser(constants.MASTERD_USER),
+                     master_stub.pw_uid)
+    self.assertEqual(self.resolver.LookupUser(constants.RAPI_USER),
+                     rapi_stub.pw_uid)
+
+  def testLookupForGroup(self):
+    master_stub = _StubGetgrnam(constants.MASTERD_GROUP)
+    rapi_stub = _StubGetgrnam(constants.RAPI_GROUP)
+    self.assertEqual(self.resolver.LookupGid(master_stub.gr_gid),
+                     constants.MASTERD_GROUP)
+    self.assertEqual(self.resolver.LookupGid(rapi_stub.gr_gid),
+                     constants.RAPI_GROUP)
+
+  def testLookupForUserNotFound(self):
+    self.assertRaises(errors.ConfigurationError, self.resolver.LookupUid, 9999)
+    self.assertRaises(errors.ConfigurationError,
+                      self.resolver.LookupUser, "does-not-exist-foo")
+
+  def testLookupForGroupNotFound(self):
+    self.assertRaises(errors.ConfigurationError, self.resolver.LookupGid, 9999)
+    self.assertRaises(errors.ConfigurationError,
+                      self.resolver.LookupGroup, "does-not-exist-foo")
+
 
 if __name__ == "__main__":
   testutils.GanetiTestProgram()
diff --git a/test/ganeti.utils.algo_unittest.py b/test/ganeti.utils.algo_unittest.py
index b4e3a645354f5ab929be07511c91668a27c79096..96b4ff5a0d44c40b51cdb29e2b68c9ac4d9c3e11 100755
--- a/test/ganeti.utils.algo_unittest.py
+++ b/test/ganeti.utils.algo_unittest.py
@@ -229,6 +229,13 @@ class TestNiceSort(unittest.TestCase):
                       None, ""])
 
 
+class TestInvertDict(unittest.TestCase):
+  def testInvertDict(self):
+    test_dict = { "foo": 1, "bar": 2, "baz": 5 }
+    self.assertEqual(algo.InvertDict(test_dict),
+                     { 1: "foo", 2: "bar", 5: "baz"})
+
+
 class TimeMock:
   def __init__(self, values):
     self.values = values