RPG-ILE - Converting Decimal to Octal values for STAT function
Background
When you extract data with the STAT function from IFS objects, it returns a lot of information. Unfortunately, it becomes difficult to always tie that information to what you will see on the screen. One of the challenges I had was to write program in RPG that pulled information out similar to the QRYIFSLIB freeware tool. The problem came in when I wanted to know what permissions were loaded on the file or directory. This procedure will show:
- how you can translate the value received from stat for the file mode into an octal value - How to tie the octal value to the permissions
Problem definition
When you run the stat function on an IFS object, one of the very first fields (usually named st_mode) returns a decimal value. The value corresponds to the actual permissions on the file. However, since it is retrieved as a decimal value, it involves having a large number of permutations to make up the rwxrwxrwx permission structure. For example:
I have the following files with their respective permissions in an IFS directory:
---------- Jan 25 17:06 testfile1 ---x------ Jan 25 17:11 testfile10 -rw------- Jan 26 09:38 testfile11 -rwx------ Jan 26 09:38 testfile12 -r-x------ Jan 26 09:38 testfile13 --wx------ Jan 26 09:38 testfile14 ----rw---- Jan 26 09:38 testfile15
The permissions list here will each correspond to a specific decimal number:
st_Mode = 32768 => return '---------'; st_Mode = 32769 => return '--------x'; st_Mode = 32770 => return '-------w-'; st_Mode = 32776 => return '-----x---'; etc
To complicate it further, the decimal value for directories and other types of IFS objects (link symbolic links), also have different sets of numbers. In the end, if you want to program for each and every permutation, it will become a very complex and large program.
The permission structure on the IFS is shown on the screen as drwxrwxrwx, and is divided into 4 groups. See this link for more info: [1]
- Position 1 - Usually indicate special permissions, such as "d" for directory, "l" for symbolic link, etc. - Positions 2 to 4 - Is the owner of the object's permissions - Positions 5 to 7 - Is for the permissions of the group - Positions 8 to 10 - Is for the permissions of everyone (or *PUBLIC as we use it on the IBMi platform)
For the permissions of my program, I am not interested in the position 1 value, just in positions 2 to 10. The octal value for each authority is:
0 = No Permission
1 = Execute
2 = Write
4 = Read
You determine the permissions by adding the values together, where 7 will imply all authority has been selected, and 0 will imply no authority selected. Example if the owner has read and write authority to the object, it will be 4+2 to give an octal value of 6 for the permissions, etc.
For each block, if you want to know what permissions has been enable, the octal value will return values in the format XXX, where each of the 3 positions determine whether it is for owner, group or public. If the IFS object has a value of 777 for its octal value, then it means the object has rwxrwxrwx for all 3 portions. A value of 500, will be r-x------, and a value of 744 will be rwxr--r--.
To make the process easier to determine the permissions, I have written a DecimalToOctal function that takes the st_mode value, translates it into octal, and then determine the correct permissions. It has been based on method 2 as described on this link: [2]
*======================================================================* * Subroutine.... DecimalToOctal * * Description... Take the st_mode decimal value and return an octal value* *======================================================================* dcl-proc DecimalToOctal; dcl-pi DecimalToOctal int(10); p_St_Mode int(10) value; end-pi; // dcl-s OctalVal char(15); dcl-s WorkField1 int(10); dcl-s WorkField2 int(10); dcl-s WorkField3 int(10); dcl-s LoopCount int(10); dcl-s OctalPosition int(10); // OctalPosition = 15; // will start at the right side WorkField3 = p_St_Mode; DoU WorkField3 = 0; WorkField1 = %div(WorkField3:8); WorkField2 = WorkField3 - (8 * WorkField1); %subst(OctalVal:OctalPosition:1) = %char(WorkField2); OctalPosition -= 1; WorkField3 = WorkField1; EndDo; Return %Int(OctalVal); End-proc;
The next step will be to use this returned value in a subprocedure to determine the correct permissions. The last 3 digits of the returned octal value is used, you can ignore the other digits.
*======================================================================* * Subroutine.... RetrieveAuthority * * Description... Take the st_mode value and return an RWX-based string* *======================================================================* dcl-proc RetrieveAuthority; dcl-pi RetrieveAuthority char(10); p_St_Mode int(10) value; end-pi; // dcl-s OctalChar char(15); dcl-s OctalLength int(10); dcl-s PermissionList char(9) inz('---------'); dcl-s OctalVal int(10); // OctalVal = DecimalToOctal(p_st_mode); OctalChar = %char(OctalVal); OctalLength = %len(%trim(OctalChar)); // // Determine the owner permissions here Select; When %subst(%trim(OctalChar):OctalLength - 2:1) = '7'; %subst(PermissionList:1:3) = 'rwx'; When %subst(%trim(OctalChar):OctalLength - 2:1) = '6'; %subst(PermissionList:1:3) = 'rw-'; When %subst(%trim(OctalChar):OctalLength - 2:1) = '5'; %subst(PermissionList:1:3) = 'r-x'; When %subst(%trim(OctalChar):OctalLength - 2:1) = '4'; %subst(PermissionList:1:3) = 'r--'; When %subst(%trim(OctalChar):OctalLength - 2:1) = '3'; %subst(PermissionList:1:3) = 'rw-'; When %subst(%trim(OctalChar):OctalLength - 2:1) = '2'; %subst(PermissionList:1:3) = '-w-'; When %subst(%trim(OctalChar):OctalLength - 2:1) = '1'; %subst(PermissionList:1:3) = '--x'; Other; %subst(PermissionList:1:3) = '---'; EndSl; // Determine the group permissions here Select; When %subst(%trim(OctalChar):OctalLength - 1:1) = '7'; %subst(PermissionList:4:3) = 'rwx'; When %subst(%trim(OctalChar):OctalLength - 1:1) = '6'; %subst(PermissionList:4:3) = 'rw-'; When %subst(%trim(OctalChar):OctalLength - 1:1) = '5'; %subst(PermissionList:4:3) = 'r-x'; When %subst(%trim(OctalChar):OctalLength - 1:1) = '4'; %subst(PermissionList:4:3) = 'r--'; When %subst(%trim(OctalChar):OctalLength - 1:1) = '3'; %subst(PermissionList:4:3) = 'rw-'; When %subst(%trim(OctalChar):OctalLength - 1:1) = '2'; %subst(PermissionList:4:3) = '-w-'; When %subst(%trim(OctalChar):OctalLength - 1:1) = '1'; %subst(PermissionList:4:3) = '--x'; Other; %subst(PermissionList:4:3) = '---'; EndSl; // Determine the *PUBLIC permissions here Select; When %subst(%trim(OctalChar):OctalLength:1) = '7'; %subst(PermissionList:7:3) = 'rwx'; When %subst(%trim(OctalChar):OctalLength:1) = '6'; %subst(PermissionList:7:3) = 'rw-'; When %subst(%trim(OctalChar):OctalLength:1) = '5'; %subst(PermissionList:7:3) = 'r-x'; When %subst(%trim(OctalChar):OctalLength:1) = '4'; %subst(PermissionList:7:3) = 'r--'; When %subst(%trim(OctalChar):OctalLength:1) = '3'; %subst(PermissionList:7:3) = 'rw-'; When %subst(%trim(OctalChar):OctalLength:1) = '2'; %subst(PermissionList:7:3) = '-w-'; When %subst(%trim(OctalChar):OctalLength:1) = '1'; %subst(PermissionList:7:3) = '--x'; Other; %subst(PermissionList:7:3) = '---'; EndSl; Return PermissionList; End-proc;
In order to use this in your program, do the following:
- Declare your permission variable
dcl-s Permissions char(9);
- Run your stat process against the IFS object itself
- Call the subprocedure to determine the permissions
Permissions = RetrieveAuthority(st_mode);
Examples
Example 1:
Running the RetrieveAuthority process on a decimal value of 33279 will return an octal value of 100777. Using this process will use the last 3 digits (777) and assign permissions as "rwxrwxrwx".
Example 2:
Running the RetrieveAuthority process on a decimal value of 33205 will return an octal value of 100665. Using this process will use the last 3 digits (665) and assign permissions as "rw-rw-r-x".
Example 3:
Running the RetrieveAuthority process on a decimal value of 32800 will return an octal value of 100040. Using this process will use the last 3 digits (040) and assign permissions as "---r-----".