瀏覽代碼

HUE-504 [fb] Add sticky bit support.

Added sticky bit to rwx methods.
Added sticky bit to chmod form.
Added a test case to test chmod with sticky bit. First creates a test dir, then sets sticky bit, then unsets sticky bit. Asserts mode at every step.
abec 13 年之前
父節點
當前提交
3752cff

+ 3 - 1
apps/filebrowser/src/filebrowser/forms.py

@@ -107,10 +107,12 @@ class ChmodForm(forms.Form):
   other_read = BooleanField(required=False)
   other_write = BooleanField(required=False)
   other_execute = BooleanField(required=False)
+  sticky = BooleanField(required=False)
 
   names = ("user_read", "user_write", "user_execute",
       "group_read", "group_write", "group_execute",
-      "other_read", "other_write", "other_execute")
+      "other_read", "other_write", "other_execute",
+      "sticky")
 
   def __init__(self, initial):
     logging.info(dir(self))

+ 7 - 4
apps/filebrowser/src/filebrowser/lib/rwx.py

@@ -47,7 +47,8 @@ def rwxtype(mode):
 
 BITS = (stat.S_IRUSR, stat.S_IWUSR, stat.S_IXUSR,
     stat.S_IRGRP, stat.S_IWGRP, stat.S_IXGRP,
-    stat.S_IROTH, stat.S_IWOTH, stat.S_IXOTH)
+    stat.S_IROTH, stat.S_IWOTH, stat.S_IXOTH,
+    stat.S_ISVTX)
 
 def expand_mode(mode):
   return map(lambda y: bool(mode & y), BITS)
@@ -67,9 +68,11 @@ def rwx(mode):
   this is similar in spirit to the google-able "pathinfo.py".
   """
   bools = expand_mode(mode)
-  s = list("rwxrwxrwx")
-  for (i, v) in enumerate(bools):
+  s = list("rwxrwxrwxt")
+  for (i, v) in enumerate(bools[:-1]):
     if not v:
       s[i] = "-"
+  # Sticky bit should either be 't' or no char.
+  if not bools[-1]:
+    s = s[:-1]
   return rwxtype(mode) + "".join(s)
-

+ 6 - 4
apps/filebrowser/src/filebrowser/lib/rwx_test.py

@@ -28,18 +28,20 @@ class RwxTest(unittest.TestCase):
     self.assertEquals("unknown", rwx.filetype(0))
 
   def test_expand_mode(self):
-    self.assertEquals( [True, True, False, True, True, False, False, False, True], rwx.expand_mode(0661))
+    self.assertEquals( [True, True, False, True, True, False, False, False, True, False], rwx.expand_mode(0661))
+    self.assertEquals( [True, True, False, True, True, False, False, False, True, True], rwx.expand_mode(01661))
 
   def test_compress_mode(self):
-    self.assertEquals(0661, rwx.compress_mode( (True, True, False, True, True, False, False, False, True) ))
+    self.assertEquals(0661, rwx.compress_mode( (True, True, False, True, True, False, False, False, True, False) ))
+    self.assertEquals(01661, rwx.compress_mode( (True, True, False, True, True, False, False, False, True, True) ))
 
   def check_inverseness_and_uniqueness(self):
     all = set()
-    for i in range(0, 8*8*8-1):
+    for i in range(0, 2*8*8*8-1):
       t = rwx.expand_mode(i)
       self.assertEquals(i, rwx.compress_mode(t))
       all.add(t)
-    self.assertEquals(8*8*8, len(all))
+    self.assertEquals(2*8*8*8, len(all))
 
 if __name__ == "__main__":
   unittest.main()

+ 14 - 7
apps/filebrowser/src/filebrowser/templates/chmod.mako

@@ -20,8 +20,8 @@ from django.utils.translation import ugettext as _
 <%namespace name="edit" file="editor_components.mako" />
 <style>
 .table-margin {
-	padding-left:20px;
-	padding-right:20px;
+    padding-left:20px;
+    padding-right:20px;
 }
 </style>
 
@@ -31,7 +31,7 @@ from django.utils.translation import ugettext as _
         <a href="#" class="close" data-dismiss="modal">&times;</a>
         <h3>${_('Change Permissions:')} ${path}</h3>
     </div>
-    <div class="table-margin">
+    <div class="modal-body table-margin">
         ${edit.render_field(form["path"], hidden=True)}
         <table class="table table-striped">
             <thead>
@@ -40,7 +40,8 @@ from django.utils.translation import ugettext as _
                 <th class="center">${_('User')}</th>
                 <th class="center">${_('Group')}</th>
                 <th class="center">${_('Other')}</th>
-				<th width="120">&nbsp</th>
+                <th class="center">&nbsp;</th>
+                <th width="120">&nbsp</th>
             </tr>
             </thead>
             <tbody>
@@ -49,21 +50,27 @@ from django.utils.translation import ugettext as _
                 <td class="center">${edit.render_field(form["user_read"], tag="checkbox", button_text=" ", nolabel=True)}</td>
                 <td class="center">${edit.render_field(form["group_read"], tag="checkbox", button_text=" ", nolabel=True)}</td>
                 <td class="center">${edit.render_field(form["other_read"], tag="checkbox", button_text=" ", nolabel=True)}</td>
-				<td>&nbsp;</td>
+                <td colspan="2">&nbsp;</td>
             </tr>
             <tr>
                 <td><strong>${_('Write')}</strong></td>
                 <td class="center">${edit.render_field(form["user_write"], tag="checkbox", button_text=" ", nolabel=True)}</td>
                 <td class="center">${edit.render_field(form["group_write"], tag="checkbox", button_text=" ", nolabel=True)}</td>
                 <td class="center">${edit.render_field(form["other_write"], tag="checkbox", button_text=" ", nolabel=True)}</td>
-				<td>&nbsp;</td>
+                <td colspan="2">&nbsp;</td>
             </tr>
             <tr>
                 <td><strong>${_('Execute')}</strong></td>
                 <td class="center">${edit.render_field(form["user_execute"], tag="checkbox", button_text=" ", nolabel=True)}</td>
                 <td class="center">${edit.render_field(form["group_execute"], tag="checkbox", button_text=" ", nolabel=True)}</td>
                 <td class="center">${edit.render_field(form["other_execute"], tag="checkbox", button_text=" ", nolabel=True)}</td>
-				<td>&nbsp;</td>
+                <td colspan="2">&nbsp;</td>
+            </tr>
+            <tr>
+                <td><strong>${_('Sticky')}</strong></td>
+                <td colspan="3">&nbsp;</td>
+                <td class="center">${edit.render_field(form["sticky"], tag="checkbox", button_text=" ", nolabel=True)}</td>
+                <td>&nbsp;</td>
             </tr>
             </tbody>
         </table>

+ 45 - 0
apps/filebrowser/src/filebrowser/views_test.py

@@ -25,6 +25,7 @@ from avro import schema, datafile, io
 from desktop.lib.django_test_util import make_logged_in_client
 from desktop.lib.django_util import PopupException
 from nose.tools import assert_true, assert_false, assert_equal, assert_raises
+from lib.rwx import expand_mode
 
 try:
   import json
@@ -68,6 +69,50 @@ def test_mkdir_singledir():
     except:
       pass      # Don't let cleanup errors mask earlier failures
 
+
+@attr('requires_hadoop')
+def test_chmod_sticky():
+  cluster = pseudo_hdfs4.shared_cluster()
+
+  try:
+    c = make_logged_in_client(cluster.superuser)
+    cluster.fs.setuser(cluster.superuser)
+
+    PATH = "/chmod_test"
+    cluster.fs.mkdir(PATH)
+
+    # Get current mode and make sure sticky bit is off
+    mode = expand_mode( int(cluster.fs.stats(PATH)["mode"]) )
+    assert_equal(False, mode[-1])
+
+    # Setup post data
+    permissions = ('user_read', 'user_write', 'user_execute',
+        'group_read', 'group_write', 'group_execute',
+        'other_read', 'other_write', 'other_execute',
+        'sticky') # Order matters!
+    permissions_dict = dict(filter(lambda x: x[1], zip(permissions, map(lambda x: 'on' if x else '', mode))))
+    permissions_dict['sticky'] = 'on'
+    kwargs = {'path': PATH}
+    kwargs.update(permissions_dict)
+
+    # Set sticky bit, then check sticky bit is on in hdfs
+    response = c.post("/filebrowser/chmod", kwargs)
+    mode = expand_mode( int(cluster.fs.stats(PATH)["mode"]) )
+    assert_equal(True, mode[-1])
+
+    # Unset sticky bit, then check sticky bit is off in hdfs
+    del kwargs['sticky']
+    response = c.post("/filebrowser/chmod", kwargs)
+    mode = expand_mode( int(cluster.fs.stats(PATH)["mode"]) )
+    assert_equal(False, mode[-1])
+
+  finally:
+    try:
+      cluster.fs.rmtree(PATH)     # Clean up
+    except:
+      pass      # Don't let cleanup errors mask earlier failures
+
+
 @attr('requires_hadoop')
 def test_chown():
   cluster = pseudo_hdfs4.shared_cluster()