source: proiecte/HadoopJUnit/hadoop-0.20.1/src/test/org/apache/hadoop/hdfs/TestDFSPermission.java @ 120

Last change on this file since 120 was 120, checked in by (none), 14 years ago

Added the mail files for the Hadoop JUNit Project

  • Property svn:executable set to *
File size: 35.3 KB
Line 
1/**
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements.  See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership.  The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License.  You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18package org.apache.hadoop.hdfs;
19
20import java.io.IOException;
21import java.util.Random;
22
23import javax.security.auth.login.LoginException;
24
25import org.apache.commons.logging.*;
26import org.apache.hadoop.conf.Configuration;
27import org.apache.hadoop.hdfs.MiniDFSCluster;
28import org.apache.hadoop.hdfs.server.common.Util;
29import org.apache.hadoop.fs.*;
30import org.apache.hadoop.fs.permission.*;
31import org.apache.hadoop.security.AccessControlException;
32import org.apache.hadoop.security.UnixUserGroupInformation;
33
34import junit.framework.AssertionFailedError;
35import junit.framework.TestCase;
36
37/** Unit tests for permission */
38public class TestDFSPermission extends TestCase {
39  public static final Log LOG = LogFactory.getLog(TestDFSPermission.class);
40  final private static Configuration conf = new Configuration();
41 
42  final private static String GROUP1_NAME = "group1";
43  final private static String GROUP2_NAME = "group2";
44  final private static String GROUP3_NAME = "group3";
45  final private static String GROUP4_NAME = "group4";
46  final private static String USER1_NAME = "user1";
47  final private static String USER2_NAME = "user2";
48  final private static String USER3_NAME = "user3";
49
50  private static UnixUserGroupInformation SUPERUSER;
51  private static UnixUserGroupInformation USER1;
52  private static UnixUserGroupInformation USER2;
53  private static UnixUserGroupInformation USER3;
54 
55  final private static short MAX_PERMISSION = 511;
56  final private static short DEFAULT_UMASK = 022;
57  final private static short FILE_MASK = 0666;
58  final private static FsPermission DEFAULT_PERMISSION = 
59    FsPermission.createImmutable((short) 0777);
60  final static private int NUM_TEST_PERMISSIONS = 
61    conf.getInt("test.dfs.permission.num", 10) * (MAX_PERMISSION + 1) / 100;
62
63
64  final private static String PATH_NAME = "xx";
65  final private static Path FILE_DIR_PATH = new Path("/", PATH_NAME);
66  final private static Path NON_EXISTENT_PATH = new Path("/parent", PATH_NAME);
67  final private static Path NON_EXISTENT_FILE = new Path("/NonExistentFile");
68
69  private FileSystem fs;
70  private static Random r;
71
72  static {
73    try {
74      // Initiate the random number generator and logging the seed
75      long seed = Util.now();
76      r = new Random(seed);
77      LOG.info("Random number generator uses seed " + seed);
78      LOG.info("NUM_TEST_PERMISSIONS=" + NUM_TEST_PERMISSIONS);
79     
80      // explicitly turn on permission checking
81      conf.setBoolean("dfs.permissions", true);
82     
83      // Initiate all four users
84      SUPERUSER = UnixUserGroupInformation.login(conf);
85      USER1 = new UnixUserGroupInformation(USER1_NAME, new String[] {
86          GROUP1_NAME, GROUP2_NAME });
87      USER2 = new UnixUserGroupInformation(USER2_NAME, new String[] {
88          GROUP2_NAME, GROUP3_NAME });
89      USER3 = new UnixUserGroupInformation(USER3_NAME, new String[] {
90          GROUP3_NAME, GROUP4_NAME });
91    } catch (LoginException e) {
92      throw new RuntimeException(e);
93    }
94  }
95
96  /** This tests if permission setting in create, mkdir, and
97   * setPermission works correctly
98   */
99  public void testPermissionSetting() throws Exception {
100    MiniDFSCluster cluster = new MiniDFSCluster(conf, 3, true, null);
101    try {
102      cluster.waitActive();
103      fs = FileSystem.get(conf);
104      LOG.info("ROOT=" + fs.getFileStatus(new Path("/")));
105      testPermissionSetting(OpType.CREATE); // test file creation
106      testPermissionSetting(OpType.MKDIRS); // test directory creation
107    } finally {
108      fs.close();
109      cluster.shutdown();
110    }
111  }
112
113  /* check permission setting works correctly for file or directory */
114  private void testPermissionSetting(OpType op) throws Exception {
115    // case 1: use default permission but all possible umasks
116    PermissionGenerator generator = new PermissionGenerator(r);
117    for (short i = 0; i < NUM_TEST_PERMISSIONS; i++) {
118      createAndCheckPermission(op, FILE_DIR_PATH, generator.next(),
119          new FsPermission(DEFAULT_PERMISSION), true);
120    }
121
122    // case 2: use permission 0643 and the default umask
123    createAndCheckPermission(op, FILE_DIR_PATH, DEFAULT_UMASK,
124        new FsPermission((short) 0643), true);
125
126    // case 3: use permission 0643 and umask 0222
127    createAndCheckPermission(op, FILE_DIR_PATH, (short) 0222, 
128        new FsPermission((short) 0643), false);
129
130    // case 4: set permission
131    fs.setPermission(FILE_DIR_PATH, new FsPermission((short) 0111));
132    short expectedPermission = (short) ((op == OpType.CREATE) ? 0 : 0111);
133    checkPermission(FILE_DIR_PATH, expectedPermission, true);
134
135    // case 5: test non-existent parent directory
136    assertFalse(fs.exists(NON_EXISTENT_PATH));
137    createAndCheckPermission(op, NON_EXISTENT_PATH, DEFAULT_UMASK,
138        new FsPermission(DEFAULT_PERMISSION), false);
139    Path parent = NON_EXISTENT_PATH.getParent();
140    checkPermission(parent, getPermission(parent.getParent()), true);
141  }
142
143  /* get the permission of a file/directory */
144  private short getPermission(Path path) throws IOException {
145    return fs.getFileStatus(path).getPermission().toShort();
146  }
147
148  /* create a file/directory with the default umask and permission */
149  private void create(OpType op, Path name) throws IOException {
150    create(op, name, DEFAULT_UMASK, new FsPermission(DEFAULT_PERMISSION));
151  }
152
153  /* create a file/directory with the given umask and permission */
154  private void create(OpType op, Path name, short umask, 
155      FsPermission permission) throws IOException {
156    // set umask in configuration
157    conf.setInt(FsPermission.UMASK_LABEL, umask);
158
159    // create the file/directory
160    switch (op) {
161    case CREATE:
162      FSDataOutputStream out = fs.create(name, permission, true, conf.getInt(
163          "io.file.buffer.size", 4096), fs.getDefaultReplication(), fs
164          .getDefaultBlockSize(), null);
165      out.close();
166      break;
167    case MKDIRS:
168      fs.mkdirs(name, permission);
169      break;
170    default:
171      throw new IOException("Unsupported operation: " + op);
172    }
173  }
174
175  /* create file/directory with the provided umask and permission; then it
176   * checks if the permission is set correctly;
177   * If the delete flag is true, delete the file afterwards; otherwise leave
178   * it in the file system.
179   */
180  private void createAndCheckPermission(OpType op, Path name, short umask,
181      FsPermission permission, boolean delete) throws Exception {
182    // create the file/directory
183    create(op, name, umask, permission);
184
185    // get the short form of the permission
186    short permissionNum = (DEFAULT_PERMISSION.equals(permission)) ? MAX_PERMISSION
187        : permission.toShort();
188
189    // get the expected permission
190    short expectedPermission = (op == OpType.CREATE) ? (short) (~umask
191        & permissionNum & FILE_MASK) : (short) (~umask & permissionNum);
192
193    // check if permission is correctly set
194    checkPermission(name, expectedPermission, delete);
195  }
196
197  /* Check if the permission of a file/directory is the same as the
198   * expected permission; If the delete flag is true, delete the
199   * file/directory afterwards.
200   */
201  private void checkPermission(Path name, short expectedPermission,
202      boolean delete) throws IOException {
203    try {
204      // check its permission
205      assertEquals(getPermission(name), expectedPermission);
206    } finally {
207      // delete the file
208      if (delete) {
209        fs.delete(name, true);
210      }
211    }
212  }
213
214  /* check if the ownership of a file/directory is set correctly */
215  public void testOwnership() throws Exception {
216    MiniDFSCluster cluster = new MiniDFSCluster(conf, 3, true, null);
217    try {
218      cluster.waitActive();
219      testOwnership(OpType.CREATE); // test file creation
220      testOwnership(OpType.MKDIRS); // test directory creation
221    } finally {
222      fs.close();
223      cluster.shutdown();
224    }
225  }
226
227  /* change a file/directory's owner and group.
228   * if expectDeny is set, expect an AccessControlException.
229   */
230  private void setOwner(Path path, String owner, String group,
231      boolean expectDeny) throws IOException {
232    try {
233      String expectedOwner = (owner == null) ? getOwner(path) : owner;
234      String expectedGroup = (group == null) ? getGroup(path) : group;
235      fs.setOwner(path, owner, group);
236      checkOwnership(path, expectedOwner, expectedGroup);
237      assertFalse(expectDeny);
238    } catch(AccessControlException e) {
239      assertTrue(expectDeny);
240    }
241  }
242
243  /* check ownership is set correctly for a file or directory */
244  private void testOwnership(OpType op) throws Exception {
245    // case 1: superuser create a file/directory
246    fs = FileSystem.get(conf);
247    create(op, FILE_DIR_PATH, DEFAULT_UMASK,
248        new FsPermission(DEFAULT_PERMISSION));
249    checkOwnership(FILE_DIR_PATH, SUPERUSER.getUserName(),
250        getGroup(FILE_DIR_PATH.getParent()));
251
252    // case 2: superuser changes FILE_DIR_PATH's owner to be <user1, group3>
253    setOwner(FILE_DIR_PATH, USER1.getUserName(), GROUP3_NAME, false);
254
255    // case 3: user1 changes FILE_DIR_PATH's owner to be user2
256    login(USER1);
257    setOwner(FILE_DIR_PATH, USER2.getUserName(), null, true);
258
259    // case 4: user1 changes FILE_DIR_PATH's group to be group1 which it belongs
260    // to
261    setOwner(FILE_DIR_PATH, null, GROUP1_NAME, false);
262
263    // case 5: user1 changes FILE_DIR_PATH's group to be group3
264    // which it does not belong to
265    setOwner(FILE_DIR_PATH, null, GROUP3_NAME, true);
266
267    // case 6: user2 (non-owner) changes FILE_DIR_PATH's group to be group3
268    login(USER2);
269    setOwner(FILE_DIR_PATH, null, GROUP3_NAME, true);
270
271    // case 7: user2 (non-owner) changes FILE_DIR_PATH's user to be user2
272    setOwner(FILE_DIR_PATH, USER2.getUserName(), null, true);
273
274    // delete the file/directory
275    login(SUPERUSER);
276    fs.delete(FILE_DIR_PATH, true);
277  }
278
279  /* Return the group owner of the file/directory */
280  private String getGroup(Path path) throws IOException {
281    return fs.getFileStatus(path).getGroup();
282  }
283
284  /* Return the file owner of the file/directory */
285  private String getOwner(Path path) throws IOException {
286    return fs.getFileStatus(path).getOwner();
287  }
288
289  /* check if ownership is set correctly */
290  private void checkOwnership(Path name, String expectedOwner,
291      String expectedGroup) throws IOException {
292    // check its owner and group
293    FileStatus status = fs.getFileStatus(name);
294    assertEquals(status.getOwner(), expectedOwner);
295    assertEquals(status.getGroup(), expectedGroup);
296  }
297
298  final static private String ANCESTOR_NAME = "/ancestor";
299  final static private String PARENT_NAME = "parent";
300  final static private String FILE_NAME = "file";
301  final static private String DIR_NAME = "dir";
302  final static private String FILE_DIR_NAME = "filedir";
303
304  private enum OpType {CREATE, MKDIRS, OPEN, SET_REPLICATION,
305    GET_FILEINFO, IS_DIR, EXISTS, GET_CONTENT_LENGTH, LIST, RENAME, DELETE
306  };
307
308  /* Check if namenode performs permission checking correctly for
309   * superuser, file owner, group owner, and other users */
310  public void testPermissionChecking() throws Exception {
311    MiniDFSCluster cluster = new MiniDFSCluster(conf, 3, true, null);
312    try {
313      cluster.waitActive();
314      fs = FileSystem.get(conf);
315
316      // set the permission of the root to be world-wide rwx
317      fs.setPermission(new Path("/"), new FsPermission((short)0777));
318     
319      // create a directory hierarchy and sets random permission for each inode
320      PermissionGenerator ancestorPermissionGenerator = 
321        new PermissionGenerator(r);
322      PermissionGenerator dirPermissionGenerator = new PermissionGenerator(r);
323      PermissionGenerator filePermissionGenerator = new PermissionGenerator(r);
324      short[] ancestorPermissions = new short[NUM_TEST_PERMISSIONS];
325      short[] parentPermissions = new short[NUM_TEST_PERMISSIONS];
326      short[] permissions = new short[NUM_TEST_PERMISSIONS];
327      Path[] ancestorPaths = new Path[NUM_TEST_PERMISSIONS];
328      Path[] parentPaths = new Path[NUM_TEST_PERMISSIONS];
329      Path[] filePaths = new Path[NUM_TEST_PERMISSIONS];
330      Path[] dirPaths = new Path[NUM_TEST_PERMISSIONS];
331      for (int i = 0; i < NUM_TEST_PERMISSIONS; i++) {
332        // create ancestor directory
333        ancestorPaths[i] = new Path(ANCESTOR_NAME + i);
334        create(OpType.MKDIRS, ancestorPaths[i]);
335        fs.setOwner(ancestorPaths[i], USER1_NAME, GROUP2_NAME);
336        // create parent directory
337        parentPaths[i] = new Path(ancestorPaths[i], PARENT_NAME + i);
338        create(OpType.MKDIRS, parentPaths[i]);
339        // change parent directory's ownership to be user1
340        fs.setOwner(parentPaths[i], USER1_NAME, GROUP2_NAME);
341
342        filePaths[i] = new Path(parentPaths[i], FILE_NAME + i);
343        dirPaths[i] = new Path(parentPaths[i], DIR_NAME + i);
344
345        // makes sure that each inode at the same level
346        // has a different permission
347        ancestorPermissions[i] = ancestorPermissionGenerator.next();
348        parentPermissions[i] = dirPermissionGenerator.next();
349        permissions[i] = filePermissionGenerator.next();
350        fs.setPermission(ancestorPaths[i], new FsPermission(
351            ancestorPermissions[i]));
352        fs.setPermission(parentPaths[i], new FsPermission(
353                parentPermissions[i]));
354      }
355
356      /* file owner */
357      testPermissionCheckingPerUser(USER1, ancestorPermissions,
358          parentPermissions, permissions, parentPaths, filePaths, dirPaths);
359      /* group owner */
360      testPermissionCheckingPerUser(USER2, ancestorPermissions,
361          parentPermissions, permissions, parentPaths, filePaths, dirPaths);
362      /* other owner */
363      testPermissionCheckingPerUser(USER3, ancestorPermissions,
364          parentPermissions, permissions, parentPaths, filePaths, dirPaths);
365      /* super owner */
366      testPermissionCheckingPerUser(SUPERUSER, ancestorPermissions,
367          parentPermissions, permissions, parentPaths, filePaths, dirPaths);
368    } finally {
369      fs.close();
370      cluster.shutdown();
371    }
372  }
373
374  /* Check if namenode performs permission checking correctly
375   * for the given user for operations mkdir, open, setReplication,
376   * getFileInfo, isDirectory, exists, getContentLength, list, rename,
377   * and delete */
378  private void testPermissionCheckingPerUser(UnixUserGroupInformation ugi,
379      short[] ancestorPermission, short[] parentPermission,
380      short[] filePermission, Path[] parentDirs, Path[] files, Path[] dirs)
381      throws Exception {
382    login(SUPERUSER);
383    for (int i = 0; i < NUM_TEST_PERMISSIONS; i++) {
384      create(OpType.CREATE, files[i]);
385      create(OpType.MKDIRS, dirs[i]);
386      fs.setOwner(files[i], USER1_NAME, GROUP2_NAME);
387      fs.setOwner(dirs[i], USER1_NAME, GROUP2_NAME);
388      checkOwnership(dirs[i], USER1_NAME, GROUP2_NAME);
389      checkOwnership(files[i], USER1_NAME, GROUP2_NAME);
390
391      FsPermission fsPermission = new FsPermission(filePermission[i]);
392      fs.setPermission(files[i], fsPermission);
393      fs.setPermission(dirs[i], fsPermission);
394    }
395
396    login(ugi);
397    for (int i = 0; i < NUM_TEST_PERMISSIONS; i++) {
398      testCreateMkdirs(ugi, new Path(parentDirs[i], FILE_DIR_NAME),
399          ancestorPermission[i], parentPermission[i]);
400      testOpen(ugi, files[i], ancestorPermission[i], parentPermission[i],
401          filePermission[i]);
402      testSetReplication(ugi, files[i], ancestorPermission[i],
403          parentPermission[i], filePermission[i]);
404      testSetTimes(ugi, files[i], ancestorPermission[i],
405          parentPermission[i], filePermission[i]);
406      testStats(ugi, files[i], ancestorPermission[i], parentPermission[i]);
407      testList(ugi, files[i], dirs[i], ancestorPermission[i],
408          parentPermission[i], filePermission[i]);
409      int next = i == NUM_TEST_PERMISSIONS - 1 ? 0 : i + 1;
410      testRename(ugi, files[i], files[next], ancestorPermission[i],
411          parentPermission[i], ancestorPermission[next], parentPermission[next]);
412      testDeleteFile(ugi, files[i], ancestorPermission[i], parentPermission[i]);
413      testDeleteDir(ugi, dirs[i], ancestorPermission[i], parentPermission[i],
414          filePermission[i], null);
415    }
416   
417    // test non existent file
418    checkNonExistentFile();
419  }
420
421  /* A random permission generator that guarantees that each permission
422   * value is generated only once.
423   */
424  static private class PermissionGenerator {
425    private Random r;
426    private short permissions[] = new short[MAX_PERMISSION + 1];
427    private int numLeft = MAX_PERMISSION + 1;
428
429    PermissionGenerator(Random r) {
430      this.r = r;
431      for (int i = 0; i <= MAX_PERMISSION; i++) {
432        permissions[i] = (short) i;
433      }
434    }
435
436    short next() throws IOException {
437      if (numLeft == 0) {
438        throw new IOException("No more permission is avaialbe");
439      }
440      int index = r.nextInt(numLeft); // choose which permission to return
441      numLeft--; // decrement the counter
442
443      // swap the chosen permission with last available permission in the array
444      short temp = permissions[numLeft];
445      permissions[numLeft] = permissions[index];
446      permissions[index] = temp;
447
448      return permissions[numLeft];
449    }
450  }
451
452  /* A base class that verifies the permission checking is correct
453   * for an operation */
454  abstract class PermissionVerifier {
455    protected Path path;
456    protected short ancestorPermission;
457    protected short parentPermission;
458    private short permission;
459    protected short requiredAncestorPermission;
460    protected short requiredParentPermission;
461    protected short requiredPermission;
462    final static protected short opAncestorPermission = SEARCH_MASK;
463    protected short opParentPermission;
464    protected short opPermission;
465    protected UnixUserGroupInformation ugi;
466
467    /* initialize */
468    protected void set(Path path, short ancestorPermission,
469        short parentPermission, short permission) {
470      this.path = path;
471      this.ancestorPermission = ancestorPermission;
472      this.parentPermission = parentPermission;
473      this.permission = permission;
474      setOpPermission();
475      this.ugi = null;
476    }
477
478    /* Perform an operation and verify if the permission checking is correct */
479    void verifyPermission(UnixUserGroupInformation ugi) throws LoginException,
480        IOException {
481      if (this.ugi != ugi) {
482        setRequiredPermissions(ugi);
483        this.ugi = ugi;
484      }
485
486      try {
487        try {
488          call();
489          assertFalse(expectPermissionDeny());
490        } catch(AccessControlException e) {
491          assertTrue(expectPermissionDeny());
492        }
493      } catch (AssertionFailedError ae) {
494        logPermissions();
495        throw ae;
496      }
497    }
498
499    /** Log the permissions and required permissions */
500    protected void logPermissions() {
501      LOG.info("required ancestor permission:"
502          + Integer.toOctalString(requiredAncestorPermission));
503      LOG.info("ancestor permission: "
504          + Integer.toOctalString(ancestorPermission));
505      LOG.info("required parent permission:"
506          + Integer.toOctalString(requiredParentPermission));
507      LOG.info("parent permission: " + Integer.toOctalString(parentPermission));
508      LOG.info("required permission:"
509          + Integer.toOctalString(requiredPermission));
510      LOG.info("permission: " + Integer.toOctalString(permission));
511    }
512
513    /* Return true if an AccessControlException is expected */
514    protected boolean expectPermissionDeny() {
515      return (requiredPermission & permission) != requiredPermission
516          || (requiredParentPermission & parentPermission) !=
517                            requiredParentPermission
518          || (requiredAncestorPermission & ancestorPermission) !=
519                            requiredAncestorPermission;
520    }
521
522    /* Set the permissions required to pass the permission checking */
523    protected void setRequiredPermissions(UnixUserGroupInformation ugi)
524        throws IOException {
525      if (SUPERUSER.equals(ugi)) {
526        requiredAncestorPermission = SUPER_MASK;
527        requiredParentPermission = SUPER_MASK;
528        requiredPermission = SUPER_MASK;
529      } else if (USER1.equals(ugi)) {
530        requiredAncestorPermission = (short)(opAncestorPermission & OWNER_MASK);
531        requiredParentPermission = (short)(opParentPermission & OWNER_MASK);
532        requiredPermission = (short)(opPermission & OWNER_MASK);
533      } else if (USER2.equals(ugi)) {
534        requiredAncestorPermission = (short)(opAncestorPermission & GROUP_MASK);
535        requiredParentPermission = (short)(opParentPermission & GROUP_MASK);
536        requiredPermission = (short)(opPermission & GROUP_MASK);
537      } else if (USER3.equals(ugi)) {
538        requiredAncestorPermission = (short)(opAncestorPermission & OTHER_MASK);
539        requiredParentPermission = (short)(opParentPermission & OTHER_MASK);
540        requiredPermission = (short)(opPermission & OTHER_MASK);
541      } else {
542        throw new IllegalArgumentException("Non-supported user: " + ugi);
543      }
544    }
545
546    /* Set the rwx permissions required for the operation */
547    abstract void setOpPermission();
548
549    /* Perform the operation */
550    abstract void call() throws IOException;
551  }
552
553  final static private short SUPER_MASK = 0;
554  final static private short READ_MASK = 0444;
555  final static private short WRITE_MASK = 0222;
556  final static private short SEARCH_MASK = 0111;
557  final static private short NULL_MASK = 0;
558  final static private short OWNER_MASK = 0700;
559  final static private short GROUP_MASK = 0070;
560  final static private short OTHER_MASK = 0007;
561
562  /* A class that verifies the permission checking is correct for create/mkdir*/
563  private class CreatePermissionVerifier extends PermissionVerifier {
564    private OpType opType;
565    private boolean cleanup = true;
566
567    /* initialize */
568    protected void set(Path path, OpType opType, short ancestorPermission,
569        short parentPermission) {
570      super.set(path, ancestorPermission, parentPermission, NULL_MASK);
571      setOpType(opType);
572    }
573
574    void setCleanup(boolean cleanup) {
575      this.cleanup = cleanup;
576    }
577   
578    /* set if the operation mkdir/create */
579    void setOpType(OpType opType) {
580      this.opType = opType;
581    }
582
583    @Override
584    void setOpPermission() {
585      this.opParentPermission = SEARCH_MASK | WRITE_MASK;
586    }
587
588    @Override
589    void call() throws IOException {
590      create(opType, path);
591      if (cleanup) {
592        fs.delete(path, true);
593      }
594    }
595  }
596
597  private CreatePermissionVerifier createVerifier =
598    new CreatePermissionVerifier();
599  /* test if the permission checking of create/mkdir is correct */
600  private void testCreateMkdirs(UnixUserGroupInformation ugi, Path path,
601      short ancestorPermission, short parentPermission) throws Exception {
602    createVerifier.set(path, OpType.MKDIRS, ancestorPermission,
603        parentPermission);
604    createVerifier.verifyPermission(ugi);
605    createVerifier.setOpType(OpType.CREATE);
606    createVerifier.setCleanup(false);
607    createVerifier.verifyPermission(ugi);
608    createVerifier.setCleanup(true);
609    createVerifier.verifyPermission(ugi); // test overWritten
610  }
611
612  /* A class that verifies the permission checking is correct for open */
613  private class OpenPermissionVerifier extends PermissionVerifier {
614    @Override
615    void setOpPermission() {
616      this.opParentPermission = SEARCH_MASK;
617      this.opPermission = READ_MASK;
618    }
619
620    @Override
621    void call() throws IOException {
622      FSDataInputStream in = fs.open(path);
623      in.close();
624    }
625  }
626
627  private OpenPermissionVerifier openVerifier = new OpenPermissionVerifier();
628  /* test if the permission checking of open is correct */
629  private void testOpen(UnixUserGroupInformation ugi, Path path,
630      short ancestorPermission, short parentPermission, short filePermission)
631      throws Exception {
632    openVerifier
633        .set(path, ancestorPermission, parentPermission, filePermission);
634    openVerifier.verifyPermission(ugi);
635  }
636
637  /* A class that verifies the permission checking is correct for
638   * setReplication */
639  private class SetReplicationPermissionVerifier extends PermissionVerifier {
640    @Override
641    void setOpPermission() {
642      this.opParentPermission = SEARCH_MASK;
643      this.opPermission = WRITE_MASK;
644    }
645
646    @Override
647    void call() throws IOException {
648      fs.setReplication(path, (short) 1);
649    }
650  }
651
652  private SetReplicationPermissionVerifier replicatorVerifier =
653    new SetReplicationPermissionVerifier();
654  /* test if the permission checking of setReplication is correct */
655  private void testSetReplication(UnixUserGroupInformation ugi, Path path,
656      short ancestorPermission, short parentPermission, short filePermission)
657      throws Exception {
658    replicatorVerifier.set(path, ancestorPermission, parentPermission,
659        filePermission);
660    replicatorVerifier.verifyPermission(ugi);
661  }
662
663  /* A class that verifies the permission checking is correct for
664   * setTimes */
665  private class SetTimesPermissionVerifier extends PermissionVerifier {
666    @Override
667    void setOpPermission() {
668      this.opParentPermission = SEARCH_MASK;
669      this.opPermission = WRITE_MASK;
670    }
671
672    @Override
673    void call() throws IOException {
674      fs.setTimes(path, 100, 100);
675      fs.setTimes(path, -1, 100);
676      fs.setTimes(path, 100, -1);
677    }
678  }
679
680  private SetTimesPermissionVerifier timesVerifier =
681    new SetTimesPermissionVerifier();
682  /* test if the permission checking of setReplication is correct */
683  private void testSetTimes(UnixUserGroupInformation ugi, Path path,
684      short ancestorPermission, short parentPermission, short filePermission)
685      throws Exception {
686    timesVerifier.set(path, ancestorPermission, parentPermission,
687        filePermission);
688    timesVerifier.verifyPermission(ugi);
689  }
690
691  /* A class that verifies the permission checking is correct for isDirectory,
692   * exist,  getFileInfo, getContentSummary */
693  private class StatsPermissionVerifier extends PermissionVerifier {
694    OpType opType;
695
696    /* initialize */
697    void set(Path path, OpType opType, short ancestorPermission,
698        short parentPermission) {
699      super.set(path, ancestorPermission, parentPermission, NULL_MASK);
700      setOpType(opType);
701    }
702
703    /* set if operation is getFileInfo, isDirectory, exist, getContenSummary */
704    void setOpType(OpType opType) {
705      this.opType = opType;
706    }
707
708    @Override
709    void setOpPermission() {
710      this.opParentPermission = SEARCH_MASK;
711    }
712
713    @Override
714    void call() throws IOException {
715      switch (opType) {
716      case GET_FILEINFO:
717        fs.getFileStatus(path);
718        break;
719      case IS_DIR:
720        fs.isDirectory(path);
721        break;
722      case EXISTS:
723        fs.exists(path);
724        break;
725      case GET_CONTENT_LENGTH:
726        fs.getContentSummary(path).getLength();
727        break;
728      default:
729        throw new IllegalArgumentException("Unexpected operation type: "
730            + opType);
731      }
732    }
733  }
734
735  private StatsPermissionVerifier statsVerifier = new StatsPermissionVerifier();
736  /* test if the permission checking of isDirectory, exist,
737   * getFileInfo, getContentSummary is correct */
738  private void testStats(UnixUserGroupInformation ugi, Path path,
739      short ancestorPermission, short parentPermission) throws Exception {
740    statsVerifier.set(path, OpType.GET_FILEINFO, ancestorPermission,
741        parentPermission);
742    statsVerifier.verifyPermission(ugi);
743    statsVerifier.setOpType(OpType.IS_DIR);
744    statsVerifier.verifyPermission(ugi);
745    statsVerifier.setOpType(OpType.EXISTS);
746    statsVerifier.verifyPermission(ugi);
747    statsVerifier.setOpType(OpType.GET_CONTENT_LENGTH);
748    statsVerifier.verifyPermission(ugi);
749  }
750
751  private enum InodeType {
752    FILE, DIR
753  };
754
755  /* A class that verifies the permission checking is correct for list */
756  private class ListPermissionVerifier extends PermissionVerifier {
757    private InodeType inodeType;
758
759    /* initialize */
760    void set(Path path, InodeType inodeType, short ancestorPermission,
761        short parentPermission, short permission) {
762      this.inodeType = inodeType;
763      super.set(path, ancestorPermission, parentPermission, permission);
764    }
765
766    /* set if the given path is a file/directory */
767    void setInodeType(Path path, InodeType inodeType) {
768      this.path = path;
769      this.inodeType = inodeType;
770      setOpPermission();
771      this.ugi = null;
772    }
773
774    @Override
775    void setOpPermission() {
776      this.opParentPermission = SEARCH_MASK;
777      switch (inodeType) {
778      case FILE:
779        this.opPermission = 0;
780        break;
781      case DIR:
782        this.opPermission = READ_MASK | SEARCH_MASK;
783        break;
784      default:
785        throw new IllegalArgumentException("Illegal inode type: " + inodeType);
786      }
787    }
788
789    @Override
790    void call() throws IOException {
791      fs.listStatus(path);
792    }
793  }
794
795  ListPermissionVerifier listVerifier = new ListPermissionVerifier();
796  /* test if the permission checking of list is correct */
797  private void testList(UnixUserGroupInformation ugi, Path file, Path dir,
798      short ancestorPermission, short parentPermission, short filePermission)
799      throws Exception {
800    listVerifier.set(file, InodeType.FILE, ancestorPermission,
801        parentPermission, filePermission);
802    listVerifier.verifyPermission(ugi);
803    listVerifier.setInodeType(dir, InodeType.DIR);
804    listVerifier.verifyPermission(ugi);
805  }
806
807  /* A class that verifies the permission checking is correct for rename */
808  private class RenamePermissionVerifier extends PermissionVerifier {
809    private Path dst;
810    private short dstAncestorPermission;
811    private short dstParentPermission;
812
813    /* initialize */
814    void set(Path src, short srcAncestorPermission, short srcParentPermission,
815        Path dst, short dstAncestorPermission, short dstParentPermission) {
816      super.set(src, srcAncestorPermission, srcParentPermission, NULL_MASK);
817      this.dst = dst;
818      this.dstAncestorPermission = dstAncestorPermission;
819      this.dstParentPermission = dstParentPermission;
820    }
821
822    @Override
823    void setOpPermission() {
824      opParentPermission = SEARCH_MASK | WRITE_MASK;
825    }
826
827    @Override
828    void call() throws IOException {
829      fs.rename(path, dst);
830    }
831
832    @Override
833    protected boolean expectPermissionDeny() {
834      return super.expectPermissionDeny()
835          || (requiredParentPermission & dstParentPermission) != 
836                requiredParentPermission
837          || (requiredAncestorPermission & dstAncestorPermission) != 
838                requiredAncestorPermission;
839    }
840
841    protected void logPermissions() {
842      super.logPermissions();
843      LOG.info("dst ancestor permission: "
844          + Integer.toOctalString(dstAncestorPermission));
845      LOG.info("dst parent permission: "
846          + Integer.toOctalString(dstParentPermission));
847    }
848  }
849
850  RenamePermissionVerifier renameVerifier = new RenamePermissionVerifier();
851  /* test if the permission checking of rename is correct */
852  private void testRename(UnixUserGroupInformation ugi, Path src, Path dst,
853      short srcAncestorPermission, short srcParentPermission,
854      short dstAncestorPermission, short dstParentPermission) throws Exception {
855    renameVerifier.set(src, srcAncestorPermission, srcParentPermission, dst,
856        dstAncestorPermission, dstParentPermission);
857    renameVerifier.verifyPermission(ugi);
858  }
859
860  /* A class that verifies the permission checking is correct for delete */
861  private class DeletePermissionVerifier extends PermissionVerifier {
862    void set(Path path, short ancestorPermission, short parentPermission) {
863      super.set(path, ancestorPermission, parentPermission, NULL_MASK);
864    }
865
866    @Override
867    void setOpPermission() {
868      this.opParentPermission = SEARCH_MASK | WRITE_MASK;
869    }
870
871    @Override
872    void call() throws IOException {
873      fs.delete(path, true);
874    }
875  }
876
877  /* A class that verifies the permission checking is correct for
878   * directory deletion */
879  private class DeleteDirPermissionVerifier extends DeletePermissionVerifier {
880    private short[] childPermissions;
881
882    /* initialize */
883    void set(Path path, short ancestorPermission, short parentPermission,
884        short permission, short[] childPermissions) {
885      set(path, ancestorPermission, parentPermission, permission);
886      this.childPermissions = childPermissions;
887    }
888
889    @Override
890    void setOpPermission() {
891      this.opParentPermission = SEARCH_MASK | WRITE_MASK;
892      this.opPermission = SEARCH_MASK | WRITE_MASK | READ_MASK;
893    }
894
895    @Override
896    protected boolean expectPermissionDeny() {
897      if (super.expectPermissionDeny()) {
898        return true;
899      } else {
900        if (childPermissions != null) {
901          for (short childPermission : childPermissions) {
902            if ((requiredPermission & childPermission) != requiredPermission) {
903              return true;
904            }
905          }
906        }
907        return false;
908      }
909    }
910  }
911
912  DeletePermissionVerifier fileDeletionVerifier =
913    new DeletePermissionVerifier();
914
915  /* test if the permission checking of file deletion is correct */
916  private void testDeleteFile(UnixUserGroupInformation ugi, Path file,
917      short ancestorPermission, short parentPermission) throws Exception {
918    fileDeletionVerifier.set(file, ancestorPermission, parentPermission);
919    fileDeletionVerifier.verifyPermission(ugi);
920  }
921
922  DeleteDirPermissionVerifier dirDeletionVerifier =
923    new DeleteDirPermissionVerifier();
924
925  /* test if the permission checking of directory deletion is correct */
926  private void testDeleteDir(UnixUserGroupInformation ugi, Path path,
927      short ancestorPermission, short parentPermission, short permission,
928      short[] childPermissions) throws Exception {
929    dirDeletionVerifier.set(path, ancestorPermission, parentPermission,
930        permission, childPermissions);
931    dirDeletionVerifier.verifyPermission(ugi);
932
933  }
934
935  /* log into dfs as the given user */
936  private void login(UnixUserGroupInformation ugi) throws IOException {
937    if (fs != null) {
938      fs.close();
939    }
940    UnixUserGroupInformation.saveToConf(conf,
941        UnixUserGroupInformation.UGI_PROPERTY_NAME, ugi);
942    fs = FileSystem.get(conf); // login as ugi
943  }
944
945  /* test non-existent file */
946  private void checkNonExistentFile() {
947    try {
948      assertFalse(fs.exists(NON_EXISTENT_FILE));
949    } catch (IOException e) {
950      checkNoPermissionDeny(e);
951    }
952    try {
953      fs.open(NON_EXISTENT_FILE);
954    } catch (IOException e) {
955      checkNoPermissionDeny(e);
956    }
957    try {
958      fs.setReplication(NON_EXISTENT_FILE, (short)4);
959    } catch (IOException e) {
960      checkNoPermissionDeny(e);
961    }
962    try {
963      fs.getFileStatus(NON_EXISTENT_FILE);
964    } catch (IOException e) {
965      checkNoPermissionDeny(e);
966    }
967    try {     
968      fs.getContentSummary(NON_EXISTENT_FILE).getLength();
969    } catch (IOException e) {
970      checkNoPermissionDeny(e);
971    }
972    try {
973      fs.listStatus(NON_EXISTENT_FILE);
974    } catch (IOException e) {
975      checkNoPermissionDeny(e);
976    }
977    try {
978      fs.delete(NON_EXISTENT_FILE, true);
979    } catch (IOException e) {
980      checkNoPermissionDeny(e);
981    }
982    try {
983      fs.rename(NON_EXISTENT_FILE, new Path(NON_EXISTENT_FILE+".txt"));
984    } catch (IOException e) {
985      checkNoPermissionDeny(e);
986    }
987  }
988 
989  private void checkNoPermissionDeny(IOException e) {
990    assertFalse(e instanceof AccessControlException);
991  }
992}
Note: See TracBrowser for help on using the repository browser.