HDFS ACL权限的存储与获取源码分析

前言

我们都知道HDFS 权限除了基础权限还有ACL权限,但是当我们获取Fsimage镜像文件时只能看到基础权限信息看不到ACL权限信息,那么ACL权限信息是如何获取与存储的呢?

 大概的总结是:为了节省内存,这里的ACL其实是以整型数组的形式存储在INode节点中的,而且还有单独的user 、group的Map对象与之关联,只不过这里面涉及到了一些运算。

因hadoop源码非常复杂,下面只解析ACL权限获取与存储的部分细节。

版本说明

本次分析是基于Hadoop3.0.0源码,我对Hadoop源码重新进行了编译增加了一些输出来确认下面执行的流程的正确性。源码重新编译后面会单独写个文章说明。

794b15dea0a1917715aff42819f321db.png

setfacl流程分析

a43d5a3a4b268629c84ed53919a2eea3.png

因执行命令从开始到结束执行流程过于复杂,这里从AclStorage类中的 updateINodeAcl()方法开始切入解释其过程。以执行命令为例解析其过程

hdfs dfs -setfacl -m user:user_htsc:rwx /user

当然你要是感兴趣的话可以从下面方法开始切入了解

FSNamesystem 类的 modifyAclEntries(final String src, List<AclEntry> aclSpec)方法 

->  FSDirAclOp类 modifyAclEntries(FSDirectory fsd, final String srcArg, List<AclEntry> aclSpec) 方法

-> AclStorage 类的 updateINodeAcl(INode inode, List<AclEntry> newAcl,int snapshotId)  方法 

接着就到下面步骤


1、首先执行 AclStorage 类中的 updateINodeAcl() 方法具体实现源码如下(System.out.println是为了便于验证其输出过程的正确性加的,方便理解):   

注意//后面的注释部分

public static void updateINodeAcl(INode inode, List<AclEntry> newAcl,  
      int snapshotId) throws AclException, QuotaExceededException {      
    System.out.println("--------------开始进入updateINodeAcl方法------------------");
    assert newAcl.size() >= 3;    //判断acl条目是否大于等于3,不满足则退出程序
    System.out.println("inode="+inode);    //inode=user
    System.out.println("newAcl="+newAcl);   //newAcl=[user::rwx, user:user_htsc:rwx, group::r-x, mask::rwx, other::r-x]
    System.out.println("snapshotId="+snapshotId); //snapshotId=2147483646


    FsPermission perm = inode.getFsPermission();
    System.out.println("perm="+perm);     //perm=rwxr-xr-x
    final FsPermission newPerm;
    if (!AclUtil.isMinimalAcl(newAcl)) {
      System.out.println("AclUtil.isMinimalAcl进来了吗?="+AclUtil.isMinimalAcl(newAcl)); // AclUtil.isMinimalAcl(newAcl) = false作用是 检查给定的条目是否表示最小ACL(即是否恰巧包含3个条目)。
      // This is an extended ACL.  Split entries into access vs. default.
      ScopedAclEntries scoped = new ScopedAclEntries(newAcl);
      System.out.println("scoped="+scoped);   //scoped=org.apache.hadoop.fs.permission.ScopedAclEntries@6d04863c
      List<AclEntry> accessEntries = scoped.getAccessEntries();
      System.out.println("accessEntries="+accessEntries);  // accessEntries=[user::rwx, user:user_htsc:rwx, group::r-x, mask::rwx, other::r-x]
      List<AclEntry> defaultEntries = scoped.getDefaultEntries(); 
      System.out.println("defaultEntries="+defaultEntries);  // defaultEntries=[]


      // Only directories may have a default ACL.
      if (!defaultEntries.isEmpty() && !inode.isDirectory()) {  //这步没有执行进来,跳过
        throw new AclException(
          "Invalid ACL: only directories may have a default ACL.");
      }


      // Attach entries to the feature.
      if (inode.getAclFeature() != null) {  //跳过
        inode.removeAclFeature(snapshotId);
      }
      inode.addAclFeature(createAclFeature(accessEntries, defaultEntries), snapshotId);  // ------注意这里进入了createAclFeature()方法,也是将具体ACL信息转换成整型数组的关键方法,这个放在步骤2中说明
      System.out.println("inode="+inode);
      newPerm = createFsPermissionForExtendedAcl(accessEntries, perm);
    } else {
      // This is a minimal ACL.  Remove the ACL feature if it previously had one.
      if (inode.getAclFeature() != null) {
        inode.removeAclFeature(snapshotId);
      }
      newPerm = createFsPermissionForMinimalAcl(newAcl, perm);
    }
    System.out.println("newPerm2="+newPerm);
    inode.setPermission(newPerm, snapshotId);
    System.out.println("inode="+inode);
    System.out.println("--------------结束进入updateINodeAcl方法------------------");
  }

inode.addAclFeature(createAclFeature(accessEntries, defaultEntries), snapshotId); 

是将具体的ACL信息转换成整型数字的关键步骤。

2、执行AclStorage 类中的 createAclFeature() 方法最终生成了整型数组返回,这个方法是由1中的createAclFeature(accessEntries, defaultEntries) 步骤调用

源码如下

private static AclFeature createAclFeature(List<AclEntry> accessEntries,
      List<AclEntry> defaultEntries) {
    // Pre-allocate list size for the explicit entries stored in the feature,
    // which is all entries minus the 3 entries implicitly stored in the
    // permission bits.
    System.out.println("进入了AclStorage类中的 createAclFeature方法");
    System.out.println("createAclFeature=>"+accessEntries);  // createAclFeature=>[user::rwx, user:user_htsc:rwx, group::r-x, mask::rwx, other::r-x]
    List<AclEntry> featureEntries = Lists.newArrayListWithCapacity(
      (accessEntries.size() - 3) + defaultEntries.size());  // 根据日志信息显示推算的计算规则就是减去 default的ACL条目 + user::rwx + mask::rwx + other::r-x 三个条目,这里List集合长度计算结果是2


    System.out.println("featureEntries="+featureEntries);  //featureEntries=[],因为还没有开始添加所以还是空的,但是长度已经定义好了为2


    // For the access ACL, the feature only needs to hold the named user and
    // group entries.  For a correctly sorted ACL, these will be in a
    // predictable range.
    if (!AclUtil.isMinimalAcl(accessEntries)) {   //这里依旧是判断是否是最小ACL权限条目,即三个,很显然不是,那么加了! 条件就变成了true,进入
      featureEntries.addAll(
        accessEntries.subList(1, accessEntries.size() - 2));   //这里面截取了accessEntries中的第二个到最后第三个并添加到featureEntries中,即保留 [user:user_htsc:rwx, group::r-x]
    }
    System.out.println("defaultEntries="+defaultEntries);  //defaultEntries=[] 
    // Add all default entries to the feature.
    featureEntries.addAll(defaultEntries);
    System.out.println("featureEntries  2="+featureEntries);  // featureEntries  2=[user:user_htsc:rwx, group::r-x]
    return new AclFeature(AclEntryStatusFormat.toInt(featureEntries));   // ------这步骤也是进入转换成整型数字的关键方法,AclEntryStatusFormat.toInt(List<AclEntry> aclEntries) 方法,在第3步解析
  }

3、在上步骤要执行AclEntryStatusFormat.toInt(featureEntries) 方法之前,会优先加载 AclEntryStatusFormat 类中的 LongBitFormat 类的有参构造器

即会循环执行 new LongBitFormat(name(), previous, length, 0) 方法 将SCOPE,TYPE,PERMISSION,NAMED_ENTRY_CHECK,NAME 依次带入执行 
  SCOPE(null, 1),
  TYPE(SCOPE.BITS, 2),
  PERMISSION(TYPE.BITS, 3),
  NAMED_ENTRY_CHECK(PERMISSION.BITS, 1),
  NAME(NAMED_ENTRY_CHECK.BITS, 25);                   //以上都是枚举类中的变量


  private final LongBitFormat BITS;


  private AclEntryStatusFormat(LongBitFormat previous, int length) {
    BITS = new LongBitFormat(name(), previous, length, 0);        //LongBitFormat类会分别将5个变量依次带入LongBitFormat类中的构造器执行5次,会初始化其中的变量值,这里放在3.1 步骤中分析
  }

LongBitFormat 对象是计算的关键,它是一个工具类,可以把多项小的数据值压缩在一个long类型的数据中,极大节约了空间。

3.1 LongBitFormat 类中部分源码如下(LongBitFormat是一个工具类,可以把多项小的数据值压缩在一个long类型的数据中) (计算过程如下)

变量定义如下
          private final String NAME; //状态名,可自定义 
          /** Bit offset */
          private final int OFFSET;  //该状态在 long 字节中的偏移
          /** Bit length */
          private final int LENGTH; //用多少位存储该状态关联的数字
          /** Minimum value */
          private final long MIN; //该状态关联的最小值
          /** Maximum value */
          private final long MAX; //该状态关联的最大值
          /** Bit mask */
          private final long MASK; //掩码




          构造器源码如下
          public LongBitFormat(String name, LongBitFormat previous, int length,
                           long min) {
          NAME = name;
          OFFSET = previous == null? 0: previous.OFFSET + previous.LENGTH;
          LENGTH = length;
          MIN = min;
          MAX = ((-1L) >>> (64 - LENGTH));
          MASK = MAX << OFFSET;
          System.out.println("LongBitFormat类的构造方法--------------name="+name);
          System.out.println("LongBitFormat类的构造方法--------------length="+length);
          System.out.println("LongBitFormat类的构造方法--------------min="+min);
          System.out.println("LongBitFormat类的构造方法--------------previous.OFFSET="+OFFSET);
          System.out.println("LongBitFormat类的构造方法--------------previous.LENGTH="+LENGTH);


          --------------------------执行5次过程------------------------------
          第一次将 SCOPE 变量代入
          传入的参数值为:name=SCOPE length=1,min=0  第一次为null所以 OFFSET = 0
          开始计算MAX 和 MASK 值 
          MAX = ((-1L) >>> (64 - LENGTH)) =>  ((-1L) >>> (64 - 1=63)) => (-1L) >>> (63) => 1
          MASK = 1 << 0    => 1
          第一次计算值为:name=SCOPE length=1,min=0 , MAX =1 ,MASK = 1 


          第二次将 TYPE 变量代入
          传入的参数值为:name=TYPE length=2,min=0, OFFSET=0(上次值)+1(上次长度)=1
          开始计算MAX 和 MASK 值 
          MAX = ((-1L) >>> (64 - LENGTH)) =>  ((-1L) >>> (64 - 2=62)) => (-1L) >>> (62) => 3
          MASK = 3 << 1 即 二进制值00000011 向左偏移一位右边补0 => 00000110 转成十进制值为 6
          第二次计算值为:name=TYPE length=2,min=0 , MAX =3 ,MASK = 6




          第三次将 PERMISSION 变量代入
          传入的参数值为:name=PERMISSION length=3,min=0, OFFSET=1(上次值)+2(上次长度)=3
          开始计算MAX 和 MASK 值 
          MAX = ((-1L) >>> (64 - LENGTH)) =>  ((-1L) >>> (64 - 3=61)) => (-1L) >>> (61) => 7
          MASK = 7 << 3 即 二进制值 000000111 向左偏移三位右边补0 => 00111000 转成十进制值为 56
          第三次计算值为:name=PERMISSION length=3,min=0 , MAX =7 ,MASK = 56


          第四次将 NAMED_ENTRY_CHECK 变量代入
          传入的参数值为:name=NAMED_ENTRY_CHECK length=1,min=0, OFFSET=3(上次值)+3(上次长度)=6
          开始计算MAX 和 MASK 值 
          MAX = ((-1L) >>> (64 - LENGTH)) =>  ((-1L) >>> (64 - 1=63)) => (-1L) >>> (63) => 1
          MASK = 1 << 6 即 二进制值 00000001 向左偏移六位右边补0 => 01000000 转成十进制值为 64
          第四次计算值为:name=NAMED_ENTRY_CHECK length=1,min=0 , MAX =1 ,MASK = 64




          第五次将 NAME 变量代入
          传入的参数值为:name=NAME length=25,min=0, OFFSET=6(上次值)+1(上次长度)=7
          开始计算MAX 和 MASK 值 
          MAX = ((-1L) >>> (64 - LENGTH)) =>  ((-1L) >>> (64 - 25 =39)) => (-1L) >>> (39) =>  33554431     
          MASK = 33554431 << 7 即 4294967168
          第五次计算值为:name=NAMED_ENTRY_CHECK length=1,min=0 , MAX = 33554431 ,MASK = 4294967168     -------这也是最终值,因为它只初始化一次


          -------------------------结束分隔符----------------------------------


  }

4、接下来接着 AclEntryStatusFormat类中 toInt(List<AclEntry> aclEntries) 方法通过遍历获取AclEntry信息,调用toInt(AclEntry aclEntry) 方法来转换成具体整型数字

public static int[] toInt(List<AclEntry> aclEntries) {  //参入的参数值 [user:user_htsc:rwx, group::r-x]
    int[] entries = new int[aclEntries.size()];
    for (int i = 0; i < entries.length; i++) {   
      entries[i] = toInt(aclEntries.get(i));   // --------关键步骤,这是将单个AclEntry中的属性值转成整型数字的关键步骤,在步骤4中分析其流程  结果是依次返回的值是376 、42即 [] entries=[376、42]
    }
    return entries;
  }

5、AclEntryStatusFormat 类 toInt(AclEntry aclEntry) 调用,将ACL权限具体属性值转换成整型数字过程,源码及解析如下

static int toInt(AclEntry aclEntry) {
    System.out.println("wqg:====进入了AclEntryStatusFormat枚举类的toInt方法中");
    System.out.println("wqg:====aclEntry.getName()="+aclEntry.getName()); //wqg:====aclEntry.getName()=user_htsc
    System.out.println("wqg:====aclEntry.getScope()="+aclEntry.getScope()); //wqg:====aclEntry.getScope()=ACCESS
    System.out.println("wqg:====aclEntry.getType()="+aclEntry.getType());  //wqg:====aclEntry.getType()=USER
    System.out.println("wqg:====aclEntry.getPermission()="+aclEntry.getPermission()); //wqg:====aclEntry.getPermission()=ALL 


    long aclEntryInt = 0;
    aclEntryInt = SCOPE.BITS                                  // aclEntry.getScope().ordinal() 就是枚举类中 ACCESS 位于第几个,下标从0开始 (可在AclEntryScope枚举类中查询) 
        .combine(aclEntry.getScope().ordinal(), aclEntryInt); // 那么这个就等同于 SCOPE.BITS.combine(0,0)  
        ---------------------------中间穿插解释SCOPE.BITS.combine(0,0)-------------------------------
        由3.1 步骤我们知道了一些变量的值 name=NAMED_ENTRY_CHECK length=1,min=0 , MAX = 1 ,MASK = 1  


        LongBitFormat类中有 combine 方法,那这个方法源码如下,我们将传入的两个值带入运算
          /** Combine the value to the record. */
        public long combine(long value, long record) {  (0,0)
          if (value < MIN) {
            throw new IllegalArgumentException(
                "Illagal value: " + NAME + " = " + value + " < MIN = " + MIN);
          }
          if (value > MAX) {
            throw new IllegalArgumentException(
                "Illagal value: " + NAME + " = " + value + " > MAX = " + MAX);
          }
          return (record & ~MASK) | (value << OFFSET);  // 计算 (0 & ~4294967168) | (0 << 0) =>  0 | 0 => 0 
  } 
        ---------------------------结束分隔符---------------------------------------------------------


    System.out.println("toInt() getScope---第一次的aclEntryInt="+aclEntryInt); // toInt() getScope---第一次的aclEntryInt=0


    aclEntryInt = TYPE.BITS.combine(aclEntry.getType().ordinal(), aclEntryInt);  //用上面同样的过程运算 AclEntryType 中的USER是枚举类变量里面的第一个下标是0,即aclEntry.getType().ordinal()=0,第二个参数也是0,那么和第一次运算一致,结果也是0
    System.out.println("toInt() getType---第二次的aclEntryInt="+aclEntryInt); //toInt() getType---第二次的aclEntryInt=0


    //第三个变量
    aclEntryInt = PERMISSION.BITS.combine(aclEntry.getPermission().ordinal(),  // aclEntry.getPermission().ordinal()可从 FsAction 类中查看 ALL变量位于第8个即下标是7 即aclEntry.getPermission().ordinal()=7
        aclEntryInt);                                                           //传入的值是PERMISSION.BITS.combine(7,0) 按照上面计算规则再次捋一遍,因为这些是转换的关键步骤,所以再啰嗦一下


       ---------------------------中间穿插解释PERMISSION.BITS.combine(7,0)-------------------------------
        由3.1 步骤我们知道了一些变量的值 name=PERMISSION length=1,min=0 ,MASK = 56  OFFSET=3
        LongBitFormat类中有 combine 方法,那这个方法源码如下,我们将传入的两个值带入运算
          /** Combine the value to the record. */
        public long combine(long value, long record) {  (7,0)
          if (value < MIN) {  7 < 0 不符合
            throw new IllegalArgumentException(
                "Illagal value: " + NAME + " = " + value + " < MIN = " + MIN);
          }
          if (value > MAX) {   7 > 16 不符合不进入
            throw new IllegalArgumentException(
                "Illagal value: " + NAME + " = " + value + " > MAX = " + MAX);
          }
          return (record & ~MASK) | (value << OFFSET);  // 计算 (0 & ~56) | (7 << 3) =>  0 | 56 => 56
  }
        ---------------------------结束分隔符--------------------------------------------------------- 


    System.out.println("toInt() getPermission ---第三次的aclEntryInt="+aclEntryInt); // toInt() getPermission ---第三次的aclEntryInt=56
    System.out.println("获取aclEntry.getName()="+aclEntry.getName());   //获取aclEntry.getName()=user_htsc
    if (aclEntry.getName() != null) {
      aclEntryInt = NAMED_ENTRY_CHECK.BITS.combine(1, aclEntryInt);  // aclEntryInt=120


       ---------------------------中间穿插解释NAMED_ENTRY_CHECK.BITS.combine(1,56)-------------------------------      
        由3.1 步骤我们知道了一些变量的值 name=NAMED_ENTRY_CHECK length=1,min=0 ,MASK = 64  OFFSET=6
        LongBitFormat类中有 combine 方法,那这个方法源码如下,我们将传入的两个值带入运算
          /** Combine the value to the record. */
        public long combine(long value, long record) {  (1,56)
          if (value < MIN) {  1 < 0 不符合
            throw new IllegalArgumentException(
                "Illagal value: " + NAME + " = " + value + " < MIN = " + MIN);
          }
          if (value > MAX) {   1 > 1 不符合不进入
            throw new IllegalArgumentException(
                "Illagal value: " + NAME + " = " + value + " > MAX = " + MAX);
          }
          return (record & ~MASK) | (value << OFFSET);  // 计算 (56 & ~64) | (1 << 6) =>  56 | 64 => 120
  }
        ---------------------------结束分隔符---------------------------------------------------------       
      System.out.println("toInt() userId ---补充的的aclEntryInt="+aclEntryInt);  // aclEntryInt = 120
      if (aclEntry.getType() == AclEntryType.USER) {
        int userId = SerialNumberManager.INSTANCE.getUserSerialNumber(aclEntry
            .getName());          // --------这步 SerialNumberManager.INSTANCE.getUserSerialNumber 这步骤是将user信息添加到SerialNumberMap集合中的t2i和 i2t
        System.out.println("userId--------="+userId);  //userId=2
        aclEntryInt = NAME.BITS.combine(userId, aclEntryInt);


     ---------------------------NAME.BITS.combine(2,120)-------------------------------      
        由3.1 步骤我们知道了一些变量的值 name=NAME length=1,min=0 ,MASK = 4294967168  OFFSET=7,MAX = 33554431
        LongBitFormat类中有 combine 方法,那这个方法源码如下,我们将传入的两个值带入运算
          /** Combine the value to the record. */
        public long combine(long value, long record) {  (2,120)
          if (value < MIN) {  2 < 0 不符合
            throw new IllegalArgumentException(
                "Illagal value: " + NAME + " = " + value + " < MIN = " + MIN);
          }
          if (value > MAX) {   2 > 33554431 不符合不进入
            throw new IllegalArgumentException(
                "Illagal value: " + NAME + " = " + value + " > MAX = " + MAX);
          }
          return (record & ~MASK) | (value << OFFSET);  // 计算 (120 & ~4294967168) | (2 << 7 ) =>  376
  }
        ---------------------------结束分隔符---------------------------------------------------------            


        System.out.println("toInt() userId ---第四次的aclEntryInt="+aclEntryInt); //toInt() userId ---第四次的aclEntryInt=376
      } else if (aclEntry.getType() == AclEntryType.GROUP) {
        System.out.println("进入了GROUP");
        int groupId = SerialNumberManager.INSTANCE
            .getGroupSerialNumber(aclEntry.getName());
        aclEntryInt = NAME.BITS.combine(groupId, aclEntryInt);
      }
    }
    System.out.println("wqg:====toInt()方法中的变量aclEntryInt="+aclEntryInt);  //wqg:====toInt()方法中的变量aclEntryInt=376
    return (int) aclEntryInt;
  }

这步结束之后就将ACL的具体信息转换成了整型数组形式,它以AclFeature对象存储在INode节点中,虽然在梳理其流程的过程中没有明确找到,但是可以大致确认。

910834e6f652342d1b9664cad21a5ef8.png

getfacl流程分析

c8a38e77f692fbfef1d5aed3daea9448.png

这里同样以此示例进行解析

[hadoop@hadoopsrca ~]$ hdfs dfs -getfacl /user
# file: /user
# owner: hadoop
# group: supergroup
user::rwx
user:user_htsc:rwx
group::r-x
mask::rwx
other::r-x

即主要分析 -getfacl 时候是如何将 AclFeature int [] entries 数组的值转换成 user:user_htsc:rwx 具体权限值的

这里主要从 AclStorage 类 getEntriesFromAclFeature()方法获取到 int [] entries值进行切入解释其过程,看看是如何通过int [] entries 获取对应user值的


当然你要是感兴趣也可以了解一下下面过程,最近中间过程断了,欢迎各位大佬补充

AclCommands 类 registerCommands()方法 -> AclCommands类 processOptions-> CommandFactory类 addClass(Class<? extends Command> cmdClass, String ... names) 执行 lassMap.put(name, cmdClass) 步骤-> 

----中间没有连接上---      -> AclCommands 类 processPath(PathData item)方法 -> 中间执行了一些方法 -> 

AclUtil 类 getAclFromPermAndEntries(FsPermission perm,List<AclEntry> entries) -> AclCommands类 printAclEntriesForSingleScope() ->打印到控制台

FSNamesystem 类 getAclStatus(String src) 方法-> FSDirAclOp 类 getAclStatus(FSDirectory fsd, String src) 方法 中 INodesInPath iip = fsd.resolvePath(pc, src, DirOp.READ); 步骤 -> 

FSDirectory类 resolvePath(FSPermissionChecker pc, String src,DirOp dirOp)方法-> 

再返回  FSDirAclOp 类 getAclStatus(FSDirectory fsd, String src) 方法 中 List<AclEntry> acl = AclStorage.readINodeAcl(fsd.getAttributes(iip)); 步骤->

AclStorage类 readINodeAcl(INodeAttributes inodeAttr) 方法 AclFeature f = inodeAttr.getAclFeature() 步骤->  AclStorage类  getEntriesFromAclFeature(AclFeature aclFeature) 也就到了下面步骤

1、先从 AclStorage 类 getEntriesFromAclFeature()方法执行开始,先获取entries整型数组的

长度,然后循环遍历得到其数组值, AclStorage 类 getEntriesFromAclFeature()方法在源码中实现如下:

static ImmutableList<AclEntry> getEntriesFromAclFeature(AclFeature aclFeature) { 
    if (aclFeature == null) {
      return ImmutableList.<AclEntry> of();
    }
    ImmutableList.Builder<AclEntry> b = new ImmutableList.Builder<AclEntry>(); 


    for (int pos = 0, entry; pos < aclFeature.getEntriesSize(); pos++) {  //循环遍历数组得到具体值,aclFeature.getEntriesSize() 对应得到AclFeature类中的 int [] entries数组长度。[376、42]
      entry = aclFeature.getEntryAt(pos);   //分别获取到的值为376、42 
      b.add(AclEntryStatusFormat.toAclEntry(entry));  //将具体整型数字(ACL对应的具体值)传入到AclEntryStatusFormat.toAclEntry()方法中,这在步骤2进行分析      --------关键步骤
      System.out.println("AclEntryStatusFormat.toAclEntry(entry)="+AclEntryStatusFormat.toAclEntry(entry)); //AclEntryStatusFormat.toAclEntry(entry)=user:user_htsc:rwx
    }
    return b.build();
  }

2、 接着上面执行 AclEntryStatusFormat 类中的 toAclEntry() 方法 ,其源码实现过程如下:

static AclEntry toAclEntry(int aclEntry) {  // 传入的值依次是376、42 ,下面按照传入值376进行举例说明
    AclEntry.Builder builder = new AclEntry.Builder();
    System.out.println("AclEntryStatusFormat类->toAclEntry()->aclEntry="+aclEntry); // aclEntry=376
    builder.setScope(getScope(aclEntry)).setType(getType(aclEntry))  //这步骤别调用了 AclEntryStatusFormat类中的  getScope() getType()  getPermission(),先继续往下走   -------------关键步骤
        .setPermission(getPermission(aclEntry));      
    if (getName(aclEntry) != null) {
      builder.setName(getName(aclEntry));  //调用了 AclEntryStatusFormat类中的  getName() ,而getScope() getType()  getPermission()  getName()下面会进行分别进行解析 ,对应步骤从2.1 - 2.4  ------------关键步骤
    }
    System.out.println("走进AclEntryStatusFormat类->toAclEntry()方法->b.build().toString()="+builder.build().toString()); // builder.build().toString() 对应的值是 user:user_htsc:rwx
    return builder.build();
  }

2.1 先分析getScope()方法,getScope

 主要获取的值为 ACCESS 或者是 DEFAULT即 对应目录中的有效ACL权限和DEFAULT权限,源码过程如下

static AclEntryScope getScope(int aclEntry) {
    int ordinal = (int) SCOPE.BITS.retrieve(aclEntry); ----------关键步骤
    -----------------------------------解析SCOPE.BITS.retrieve(aclEntry)------------------------------------------------
    这里由setfacl时候其实是执行过一次了,因为是final static修饰,其值不会发生变化 ,根据将SCOPE代入得到的 MASK = 1, OFFSET=0
     retrieve源码实现如下:
      /** Retrieve the value from the record. */
      public long retrieve(long record) {  //传入的值是376
        return (record & MASK) >>> OFFSET;  // (376 & 1 ) >>> 0 => 0 >>>0  => 0
      }  
    ----------------------------------------结束分隔符------------------------------------------------------------------
    return AclEntryScope.values()[ordinal];  //AclEntryScope.values()[ordinal] 直接获取到了其值为ACCESS 这步为关键点  AclEntryScope.values()[ordinal]=ACCESS
  }

 2.2 getType()方法,这个步骤主要获取的ACL权限类型是USER还是GROUP

static AclEntryType getType(int aclEntry) {
    int ordinal = (int) TYPE.BITS.retrieve(aclEntry); //getType()->变量ordinal=0
    -----------------------------------解析TYPE.BITS.retrieve(aclEntry)------------------------------------------------
    根据将 TYPE 代入得到的 MASK = 6, OFFSET=1
     retrieve源码实现如下:
      /** Retrieve the value from the record. */
      public long retrieve(long record) {  //传入的值是376
        return (record & MASK) >>> OFFSET;  // (376 & 6 ) >>> 0 => 0 >>>1  => 0
      }  
    ----------------------------------------结束分隔符------------------------------------------------------------------    
    return AclEntryType.values()[ordinal];  //AclEntryType.values()[ordinal]=USER     AclEntryType枚举类中第一个就是USER变量
  }

 2.3 getPermission()方法主要获取的是类似rwx这种权限,这种权限对应的常量值(FsAction类中)如下

NONE("---"),
    EXECUTE("--x"),
    WRITE("-w-"),
    WRITE_EXECUTE("-wx"),
    READ("r--"),
    READ_EXECUTE("r-x"),
    READ_WRITE("rw-"),
    ALL("rwx");
  
    static FsAction getPermission(int aclEntry) {
    int ordinal = (int) PERMISSION.BITS.retrieve(aclEntry); //0
    -----------------------------------PERMISSION.BITS.retrieve(aclEntry)------------------------------------------------
    根据将 TYPE 代入得到的 MASK = 56, OFFSET=3
     retrieve源码实现如下:
      /** Retrieve the value from the record. */
      public long retrieve(long record) {  //传入的值是376
        return (record & MASK) >>> OFFSET;  // (376 & 56 ) >>> 7 => 0 >>>3  => 7
      }  
    ----------------------------------------结束分隔符------------------------------------------------------------------       
    return FsAction.values()[ordinal]; // 得到FsAction枚举类变量里面下标为7的值即ALL对应rwx
  }

2.4 getName()方法获取的就是赋权ACL时候的用户名称,具体获取实现过程如下

static String getName(int aclEntry) {   
    int nameExists = (int) NAMED_ENTRY_CHECK.BITS.retrieve(aclEntry); //判断 用户名是否存在,不存在返回0,存在返回对应值 nameExists=1
    -----------------------------------NAMED_ENTRY_CHECK.BITS.retrieve(aclEntry)------------------------------------------------
    根据将 TYPE 代入得到的 MASK = 64, OFFSET=6
     retrieve源码实现如下:
      /** Retrieve the value from the record. */
      public long retrieve(long record) {  //传入的值是376
        return (record & MASK) >>> OFFSET;  // (376 & 64 ) >>> 6 => 64 >>> 6  => 1
      }  
    ----------------------------------------结束分隔符------------------------------------------------------------------           
    if (nameExists == 0) {
      return null;
    }
    
    int id = (int) NAME.BITS.retrieve(aclEntry);   //id=2 
    -----------------------------------NAME.BITS.retrieve(aclEntry)------------------------------------------------
    根据将 TYPE 代入得到的 MASK = 4294967168 , OFFSET= 7
     retrieve源码实现如下:
      /** Retrieve the value from the record. */
      public long retrieve(long record) {  //传入的值是376
        return (record & MASK) >>> OFFSET;  // (376 & 4294967168 ) >>> 7 => 256 >>> 7 => 2
      }  
    ----------------------------------------结束分隔符------------------------------------------------------------------       
    
    AclEntryType type = getType(aclEntry);  //获取aclEntry这个序列号对应的类型
    System.out.println("AclEntryType type="+type); // type=USER
    if (type == AclEntryType.USER) {  //当判断为user时就会去找user映射里面序号为2的值那么对应的就是 user_htsc
      return SerialNumberManager.INSTANCE.getUser(id); //这步骤根据id直接获取了user 获取的值是user_htsc下面会接着分析SerialNumberManager.INSTANCE.getUser(id) 具体过程,标注步骤--2.4.1 
    } else if (type == AclEntryType.GROUP) {
      System.out.println("SerialNumberManager.INSTANCE.getGroup(id)="+SerialNumberManager.INSTANCE.getGroup(id));
      return SerialNumberManager.INSTANCE.getGroup(id);  
    }
    return null;
  }

2.4.1  主要看下SerialNumberManager.INSTANCE.getUser(id)这步骤做了哪些事情(SerialNumberManager类是管理用户、组与序列号对应的类)

-----------------------------说明----------------------------------            
  SerialNumberManager类中有两个变量直接声明的同时创建了对象
  static final SerialNumberManager INSTANCE = new SerialNumberManager();


  private final SerialNumberMap<String> usermap = new SerialNumberMap<String>();         -------------user用户映射关系都是放在SerialNumberMap类中的,在2.4.2步骤解析
  private final SerialNumberMap<String> groupmap = new SerialNumberMap<String>();
  
   SerialNumberManager.INSTANCE就是执行的是new SerialNumberManager();
  -----------------------------结束分隔符-------------------------------        
                    
  SerialNumberManager.INSTANCE.getUser对应的源码实现过程为:
  
  String getUser(int n) {
    return usermap.get(n); // usermap.get(n) = user_htsc  这步骤直接得到了对应的用户
  }
  
  usermap变量toStrig输出的内容是:
  max=3,
  t2i={user_htsc=2, hadoop=1},
  i2t={1=hadoop, 2=user_htsc}
  
  从这里可以清晰的看到用户在usermap中对应的关系

2.4.2 SerialNumberMap 类源码如下

这个类主要存储的是用户和id的对应信息,包含根据ID查询对应用户和 在新增user之后 将其放入到t2i 和 i2t集合中。

public class SerialNumberMap<T> {
    private final AtomicInteger max = new AtomicInteger(1);
    private final ConcurrentMap<T, Integer> t2i =        //t2i  key是T类型 V是int类型 这个存储的是用户和ID关系,是根据用户名检索ID
        new ConcurrentHashMap<T, Integer>();
    private final ConcurrentMap<Integer, T> i2t =        // i2t  key是 int v是T 这个也是存储用户和ID关系,是用于根据ID检索用户名
        new ConcurrentHashMap<Integer, T>();


    public int get(T t) {           //这个方法目的是将新增的user  新增到t2i 和 i2t map集合中
      System.out.println("----------进入了SerialNumberMap类中的get(T t)方法-------------------");
      System.out.println("传入参数T="+t);
      if (t == null) {
        return 0;
      }
      System.out.println("get(T t)->i2t="+i2t);
      Integer sn = t2i.get(t);
      if (sn == null) {
        sn = max.getAndIncrement();
        if (sn < 0) {
          throw new IllegalStateException("Too many elements!");
        }
        Integer old = t2i.putIfAbsent(t, sn);
        System.out.println("old="+old);
        if (old != null) {
          return old;
        }
        i2t.put(sn, t);
        System.out.println("---->>>it2="+i2t);
      }
      return sn;
    }


    public T get(int i) {   //这个方法只用于根据ID取对应的user
      System.out.println("----------进入了SerialNumberMap类中的get(int i)方法-------------------");
      System.out.println("传入参数i="+i);
      if (i == 0) {
        return null;
      }
      System.out.println("get(int i)->i2t="+i2t);
      T t = i2t.get(i);
      System.out.println("t="+t);
      if (t == null) {
        throw new IllegalStateException("!i2t.containsKey(" + i
            + "), this=" + this);
      }
      return t;
    }


    @Override
    public String toString() {
      return "max=" + max + ",\n  t2i=" + t2i + ",\n  i2t=" + i2t;
    }
  }


  知道了这个类的作用是添加用户ID或者组ID 对应关系到MAP集合中  和 根据ID获取对应USER或者GROUP 名字
  根据目前理解static final SerialNumberManager INSTANCE = new SerialNumberManager(); 这样定义 SerialNumberManager类 可以被重复调用多次,而对象只会被创建一次,因此无论是新增还是删除都是公用的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值