1#
2# Copyright 2017 - The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16import logging
17from vts.runners.host import asserts
18from vts.runners.host import const
19
20_PERMISSION_GROUPS = 3  # 3 permission groups: owner, group, all users
21_READ_PERMISSION = 4
22_WRITE_PERMISSION = 2
23_EXECUTE_PERMISSION = 1
24
25
26def _Test(shell, *args):
27    """Executes test command on device.
28
29    Args:
30        shell: an instance of the VTS shell.
31        *args: strings, the command line arguments.
32
33    Returns:
34        boolean, whether the condition is true.
35    """
36    cmd = "test %s" % " ".join(args)
37    results = shell.Execute(cmd)
38    return results[const.EXIT_CODE][0] == 0
39
40
41def Exists(filepath, shell):
42    """Determines if a file or directory exists.
43
44    Args:
45        filepath: string, the path to a file or a directory.
46        shell: an instance of the VTS shell.
47
48    Returns:
49        True if exists, False otherwise.
50    """
51    return _Test(shell, "-e", filepath)
52
53
54def IsDirectory(path, shell):
55    """Determines if a path is a directory.
56
57    Args:
58        path: string, a path on device.
59        shell: an instance of the VTS shell.
60
61    Returns:
62        True if the path is a directory, False otherwise.
63    """
64    return _Test(shell, "-d", path)
65
66
67def FindFiles(shell, path, name_pattern, options=None):
68    """Searches a path for files on device.
69
70    Args:
71        shell: the ShellMirrorObject.
72        path: string, the path to search on device.
73        name_pattern: string, the file name pattern.
74        options: string, other options passed to find command.
75
76    Returns:
77        list of strings, the paths to the found files.
78
79    Raises:
80        IOError if the pattern contains quotes, or the path does not exist.
81    """
82    if '"' in name_pattern or "'" in name_pattern:
83        raise IOError("File name pattern contains quotes")
84    cmd = "find %s -name \"%s\"" % (path, name_pattern)
85    if options is not None:
86        cmd += " " + options
87    results = shell.Execute(cmd)
88    logging.debug("%s: Shell command '%s' results: %s", path, cmd, results)
89
90    if results[const.EXIT_CODE][0] != 0:
91        raise IOError(results[const.STDERR][0])
92
93    stdout = str(results[const.STDOUT][0])
94    # Filter out empty strings before return.
95    return filter(None, stdout.strip().split("\n"))
96
97
98def ReadFileContent(filepath, shell):
99    """Read the content of a file and perform assertions.
100
101    Args:
102        filepath: string, path to file
103        shell: an instance of the VTS shell
104
105    Returns:
106        string, content of file
107
108    Raises:
109        IOError if the file does not exist.
110    """
111    cmd = "cat %s" % filepath
112    results = shell.Execute(cmd)
113    logging.debug("%s: Shell command '%s' results: %s", filepath, cmd, results)
114
115    # checks the exit code
116    if results[const.EXIT_CODE][0] != 0:
117        raise IOError(results[const.STDERR][0])
118
119    return results[const.STDOUT][0]
120
121
122def GetPermission(path, shell):
123    """Read the file permission bits of a path.
124
125    Args:
126        filepath: string, path to a file or directory
127        shell: an instance of the VTS shell
128
129    Returns:
130        String, octal permission bits for the path
131
132    Raises:
133        IOError if the path does not exist or has invalid permission bits.
134    """
135    cmd = "stat -c %%a %s" % path
136    results = shell.Execute(cmd)
137    logging.debug("%s: Shell command '%s' results: %s", path, cmd, results)
138
139    # checks the exit code
140    if results[const.EXIT_CODE][0] != 0:
141        raise IOError(results[const.STDERR][0])
142
143    accessBits = results[const.STDOUT][0].strip()
144    if len(accessBits) != 3:
145        raise IOError("%s: Wrong number of access bits (%s)" % (path,
146                                                                accessBits))
147    return accessBits
148
149
150def _HasPermission(permission_bits, groupIndex, permission):
151    """Determines if the permission bits grant a permission to a group.
152
153    Args:
154        permission_bits: string, the octal permissions string (e.g. 741)
155        groupIndex: int, the index of the group into the permissions string.
156                    (e.g. 0 is owner group). If set to -1, then all groups are
157                    checked.
158        permission: the value of the permission.
159
160    Returns:
161        True if the group(s) has read permission.
162
163    Raises:
164        ValueError if the group or permission bits are invalid
165    """
166    if groupIndex >= _PERMISSION_GROUPS:
167        raise ValueError("Invalid group: %s" % str(groupIndex))
168
169    if len(permission_bits) != _PERMISSION_GROUPS:
170        raise ValueError("Invalid permission bits: %s" % str(permission_bits))
171
172    # Define the start/end group index
173    start = groupIndex
174    end = groupIndex + 1
175    if groupIndex < 0:
176        start = 0
177        end = _PERMISSION_GROUPS
178
179    for i in range(start, end):
180        perm = int(permission_bits[i])  # throws ValueError if not an integer
181        if perm > 7:
182            raise ValueError("Invalid permission bit: %s" % str(perm))
183        if perm & permission == 0:
184            # Return false if any group lacks the permission
185            return False
186    # Return true if no group lacks the permission
187    return True
188
189
190def IsReadable(permission_bits):
191    """Determines if the permission bits grant read permission to any group.
192
193    Args:
194        permission_bits: string, the octal permissions string (e.g. 741)
195
196    Returns:
197        True if any group has read permission.
198
199    Raises:
200        ValueError if the group or permission bits are invalid
201    """
202    return any([
203        _HasPermission(permission_bits, i, _READ_PERMISSION)
204        for i in range(_PERMISSION_GROUPS)
205    ])
206
207
208def IsWritable(permission_bits):
209    """Determines if the permission bits grant write permission to any group.
210
211    Args:
212        permission_bits: string, the octal permissions string (e.g. 741)
213
214    Returns:
215        True if any group has write permission.
216
217    Raises:
218        ValueError if the group or permission bits are invalid
219    """
220    return any([
221        _HasPermission(permission_bits, i, _WRITE_PERMISSION)
222        for i in range(_PERMISSION_GROUPS)
223    ])
224
225
226def IsExecutable(permission_bits):
227    """Determines if the permission bits grant execute permission to any group.
228
229    Args:
230        permission_bits: string, the octal permissions string (e.g. 741)
231
232    Returns:
233        True if any group has execute permission.
234
235    Raises:
236        ValueError if the group or permission bits are invalid
237    """
238    return any([
239        _HasPermission(permission_bits, i, _EXECUTE_PERMISSION)
240        for i in range(_PERMISSION_GROUPS)
241    ])
242
243
244def IsReadOnly(permission_bits):
245    """Determines if the permission bits grant read-only permission.
246
247    Read-only permission is granted if some group has read access but no group
248    has write access.
249
250    Args:
251        permission_bits: string, the octal permissions string (e.g. 741)
252
253    Returns:
254        True if any group has read permission, none have write.
255
256    Raises:
257        ValueError if the group or permission bits are invalid
258    """
259    return IsReadable(permission_bits) and not IsWritable(permission_bits)
260
261
262def IsWriteOnly(permission_bits):
263    """Determines if the permission bits grant write-only permission.
264
265    Write-only permission is granted if some group has write access but no group
266    has read access.
267
268    Args:
269        permission_bits: string, the octal permissions string (e.g. 741)
270
271    Returns:
272        True if any group has write permission, none have read.
273
274    Raises:
275        ValueError if the group or permission bits are invalid
276    """
277    return IsWritable(permission_bits) and not IsReadable(permission_bits)
278
279
280def IsReadWrite(permission_bits):
281    """Determines if the permission bits grant read/write permissions.
282
283    Read-write permission is granted if some group has read access and some has
284    write access. The groups may be different.
285
286    Args:
287        permission_bits: string, the octal permissions string (e.g. 741)
288
289    Returns:
290        True if read and write permissions are granted to any group(s).
291
292    Raises:
293        ValueError if the group or permission bits are invalid
294    """
295    return IsReadable(permission_bits) and IsWritable(permission_bits)
296
297
298def assertPermissionsAndExistence(shell, path, check_permission):
299    """Asserts that the specified path exists and has the correct permission.
300
301    Args:
302        path: string, path to validate existence and permissions
303        check_permission: function which takes unix permissions in octal
304                          format and returns True if the permissions are
305                          correct, False otherwise.
306    """
307    asserts.assertTrue(Exists(path, shell), "%s: File does not exist." % path)
308    try:
309        permission = GetPermission(path, shell)
310        asserts.assertTrue(
311            check_permission(permission),
312            "%s: File has invalid permissions (%s)" % (path, permission))
313    except (ValueError, IOError) as e:
314        asserts.fail("Failed to assert permissions: %s" % str(e))
315