• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2007 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  */
16 
17 #include "devices.h"
18 
19 #include <errno.h>
20 #include <fnmatch.h>
21 #include <sys/sysmacros.h>
22 #include <unistd.h>
23 
24 #include <chrono>
25 #include <filesystem>
26 #include <memory>
27 #include <string>
28 #include <thread>
29 
30 #include <android-base/chrono_utils.h>
31 #include <android-base/file.h>
32 #include <android-base/logging.h>
33 #include <android-base/stringprintf.h>
34 #include <android-base/strings.h>
35 #include <libdm/dm.h>
36 #include <private/android_filesystem_config.h>
37 #include <selinux/android.h>
38 #include <selinux/selinux.h>
39 
40 #include "selabel.h"
41 #include "util.h"
42 
43 using namespace std::chrono_literals;
44 
45 using android::base::Basename;
46 using android::base::Dirname;
47 using android::base::ReadFileToString;
48 using android::base::Readlink;
49 using android::base::Realpath;
50 using android::base::Split;
51 using android::base::StartsWith;
52 using android::base::StringPrintf;
53 using android::base::Trim;
54 
55 namespace android {
56 namespace init {
57 
58 /* Given a path that may start with a PCI device, populate the supplied buffer
59  * with the PCI domain/bus number and the peripheral ID and return 0.
60  * If it doesn't start with a PCI device, or there is some error, return -1 */
FindPciDevicePrefix(const std::string & path,std::string * result)61 static bool FindPciDevicePrefix(const std::string& path, std::string* result) {
62     result->clear();
63 
64     if (!StartsWith(path, "/devices/pci")) return false;
65 
66     /* Beginning of the prefix is the initial "pci" after "/devices/" */
67     std::string::size_type start = 9;
68 
69     /* End of the prefix is two path '/' later, capturing the domain/bus number
70      * and the peripheral ID. Example: pci0000:00/0000:00:1f.2 */
71     auto end = path.find('/', start);
72     if (end == std::string::npos) return false;
73 
74     end = path.find('/', end + 1);
75     if (end == std::string::npos) return false;
76 
77     auto length = end - start;
78     if (length <= 4) {
79         // The minimum string that will get to this check is 'pci/', which is malformed,
80         // so return false
81         return false;
82     }
83 
84     *result = path.substr(start, length);
85     return true;
86 }
87 
88 /* Given a path that may start with a virtual block device, populate
89  * the supplied buffer with the virtual block device ID and return 0.
90  * If it doesn't start with a virtual block device, or there is some
91  * error, return -1 */
FindVbdDevicePrefix(const std::string & path,std::string * result)92 static bool FindVbdDevicePrefix(const std::string& path, std::string* result) {
93     result->clear();
94 
95     if (!StartsWith(path, "/devices/vbd-")) return false;
96 
97     /* Beginning of the prefix is the initial "vbd-" after "/devices/" */
98     std::string::size_type start = 13;
99 
100     /* End of the prefix is one path '/' later, capturing the
101        virtual block device ID. Example: 768 */
102     auto end = path.find('/', start);
103     if (end == std::string::npos) return false;
104 
105     auto length = end - start;
106     if (length == 0) return false;
107 
108     *result = path.substr(start, length);
109     return true;
110 }
111 
112 // Given a path that may start with a virtual dm block device, populate
113 // the supplied buffer with the dm module's instantiated name.
114 // If it doesn't start with a virtual block device, or there is some
115 // error, return false.
FindDmDevice(const Uevent & uevent,std::string * name,std::string * uuid)116 static bool FindDmDevice(const Uevent& uevent, std::string* name, std::string* uuid) {
117     if (!StartsWith(uevent.path, "/devices/virtual/block/dm-")) return false;
118     if (uevent.action == "remove") return false;  // Avoid error spam from ioctl
119 
120     dev_t dev = makedev(uevent.major, uevent.minor);
121 
122     auto& dm = android::dm::DeviceMapper::Instance();
123     return dm.GetDeviceNameAndUuid(dev, name, uuid);
124 }
125 
Permissions(const std::string & name,mode_t perm,uid_t uid,gid_t gid,bool no_fnm_pathname)126 Permissions::Permissions(const std::string& name, mode_t perm, uid_t uid, gid_t gid,
127                          bool no_fnm_pathname)
128     : name_(name),
129       perm_(perm),
130       uid_(uid),
131       gid_(gid),
132       prefix_(false),
133       wildcard_(false),
134       no_fnm_pathname_(no_fnm_pathname) {
135     // Set 'prefix_' or 'wildcard_' based on the below cases:
136     //
137     // 1) No '*' in 'name' -> Neither are set and Match() checks a given path for strict
138     //    equality with 'name'
139     //
140     // 2) '*' only appears as the last character in 'name' -> 'prefix'_ is set to true and
141     //    Match() checks if 'name' is a prefix of a given path.
142     //
143     // 3) '*' appears elsewhere -> 'wildcard_' is set to true and Match() uses fnmatch()
144     //    with FNM_PATHNAME to compare 'name' to a given path.
145     auto wildcard_position = name_.find('*');
146     if (wildcard_position != std::string::npos) {
147         if (wildcard_position == name_.length() - 1) {
148             prefix_ = true;
149             name_.pop_back();
150         } else {
151             wildcard_ = true;
152         }
153     }
154 }
155 
Match(const std::string & path) const156 bool Permissions::Match(const std::string& path) const {
157     if (prefix_) return StartsWith(path, name_);
158     if (wildcard_)
159         return fnmatch(name_.c_str(), path.c_str(), no_fnm_pathname_ ? 0 : FNM_PATHNAME) == 0;
160     return path == name_;
161 }
162 
MatchWithSubsystem(const std::string & path,const std::string & subsystem) const163 bool SysfsPermissions::MatchWithSubsystem(const std::string& path,
164                                           const std::string& subsystem) const {
165     std::string path_basename = Basename(path);
166     if (name().find(subsystem) != std::string::npos) {
167         if (Match("/sys/class/" + subsystem + "/" + path_basename)) return true;
168         if (Match("/sys/bus/" + subsystem + "/devices/" + path_basename)) return true;
169     }
170     return Match(path);
171 }
172 
SetPermissions(const std::string & path) const173 void SysfsPermissions::SetPermissions(const std::string& path) const {
174     std::string attribute_file = path + "/" + attribute_;
175     LOG(VERBOSE) << "fixup " << attribute_file << " " << uid() << " " << gid() << " " << std::oct
176                  << perm();
177 
178     if (access(attribute_file.c_str(), F_OK) == 0) {
179         if (chown(attribute_file.c_str(), uid(), gid()) != 0) {
180             PLOG(ERROR) << "chown(" << attribute_file << ", " << uid() << ", " << gid()
181                         << ") failed";
182         }
183         if (chmod(attribute_file.c_str(), perm()) != 0) {
184             PLOG(ERROR) << "chmod(" << attribute_file << ", " << perm() << ") failed";
185         }
186     }
187 }
188 
GetPartitionNameForDevice(const std::string & query_device)189 std::string DeviceHandler::GetPartitionNameForDevice(const std::string& query_device) {
190     static const auto partition_map = [] {
191         std::vector<std::pair<std::string, std::string>> partition_map;
192         auto parser = [&partition_map](const std::string& key, const std::string& value) {
193             if (key != "androidboot.partition_map") {
194                 return;
195             }
196             for (const auto& map : Split(value, ";")) {
197                 auto map_pieces = Split(map, ",");
198                 if (map_pieces.size() != 2) {
199                     LOG(ERROR) << "Expected a comma separated device,partition mapping, but found '"
200                                << map << "'";
201                     continue;
202                 }
203                 partition_map.emplace_back(map_pieces[0], map_pieces[1]);
204             }
205         };
206         ImportKernelCmdline(parser);
207         ImportBootconfig(parser);
208         return partition_map;
209     }();
210 
211     for (const auto& [device, partition] : partition_map) {
212         if (query_device == device) {
213             return partition;
214         }
215     }
216     return {};
217 }
218 
219 // Given a path that may start with a platform device, find the parent platform device by finding a
220 // parent directory with a 'subsystem' symlink that points to the platform bus.
221 // If it doesn't start with a platform device, return false
FindPlatformDevice(std::string path,std::string * platform_device_path) const222 bool DeviceHandler::FindPlatformDevice(std::string path, std::string* platform_device_path) const {
223     platform_device_path->clear();
224 
225     // Uevents don't contain the mount point, so we need to add it here.
226     path.insert(0, sysfs_mount_point_);
227 
228     std::string directory = Dirname(path);
229 
230     while (directory != "/" && directory != ".") {
231         std::string subsystem_link_path;
232         if (Realpath(directory + "/subsystem", &subsystem_link_path) &&
233             (subsystem_link_path == sysfs_mount_point_ + "/bus/platform" ||
234              subsystem_link_path == sysfs_mount_point_ + "/bus/amba")) {
235             // We need to remove the mount point that we added above before returning.
236             directory.erase(0, sysfs_mount_point_.size());
237             *platform_device_path = directory;
238             return true;
239         }
240 
241         auto last_slash = path.rfind('/');
242         if (last_slash == std::string::npos) return false;
243 
244         path.erase(last_slash);
245         directory = Dirname(path);
246     }
247 
248     return false;
249 }
250 
FixupSysPermissions(const std::string & upath,const std::string & subsystem) const251 void DeviceHandler::FixupSysPermissions(const std::string& upath,
252                                         const std::string& subsystem) const {
253     // upaths omit the "/sys" that paths in this list
254     // contain, so we prepend it...
255     std::string path = "/sys" + upath;
256 
257     for (const auto& s : sysfs_permissions_) {
258         if (s.MatchWithSubsystem(path, subsystem)) s.SetPermissions(path);
259     }
260 
261     if (!skip_restorecon_ && access(path.c_str(), F_OK) == 0) {
262         LOG(VERBOSE) << "restorecon_recursive: " << path;
263         if (selinux_android_restorecon(path.c_str(), SELINUX_ANDROID_RESTORECON_RECURSE) != 0) {
264             PLOG(ERROR) << "selinux_android_restorecon(" << path << ") failed";
265         }
266     }
267 }
268 
GetDevicePermissions(const std::string & path,const std::vector<std::string> & links) const269 std::tuple<mode_t, uid_t, gid_t> DeviceHandler::GetDevicePermissions(
270     const std::string& path, const std::vector<std::string>& links) const {
271     // Search the perms list in reverse so that ueventd.$hardware can override ueventd.rc.
272     for (auto it = dev_permissions_.crbegin(); it != dev_permissions_.crend(); ++it) {
273         if (it->Match(path) || std::any_of(links.cbegin(), links.cend(),
274                                            [it](const auto& link) { return it->Match(link); })) {
275             return {it->perm(), it->uid(), it->gid()};
276         }
277     }
278     /* Default if nothing found. */
279     return {0600, 0, 0};
280 }
281 
MakeDevice(const std::string & path,bool block,int major,int minor,const std::vector<std::string> & links) const282 void DeviceHandler::MakeDevice(const std::string& path, bool block, int major, int minor,
283                                const std::vector<std::string>& links) const {
284     auto[mode, uid, gid] = GetDevicePermissions(path, links);
285     mode |= (block ? S_IFBLK : S_IFCHR);
286 
287     std::string secontext;
288     if (!SelabelLookupFileContextBestMatch(path, links, mode, &secontext)) {
289         PLOG(ERROR) << "Device '" << path << "' not created; cannot find SELinux label";
290         return;
291     }
292     if (!secontext.empty()) {
293         setfscreatecon(secontext.c_str());
294     }
295 
296     gid_t new_group = -1;
297 
298     dev_t dev = makedev(major, minor);
299     /* Temporarily change egid to avoid race condition setting the gid of the
300      * device node. Unforunately changing the euid would prevent creation of
301      * some device nodes, so the uid has to be set with chown() and is still
302      * racy. Fixing the gid race at least fixed the issue with system_server
303      * opening dynamic input devices under the AID_INPUT gid. */
304     if (setegid(gid)) {
305         PLOG(ERROR) << "setegid(" << gid << ") for " << path << " device failed";
306         goto out;
307     }
308     /* If the node already exists update its SELinux label and the file mode to handle cases when
309      * it was created with the wrong context and file mode during coldboot procedure. */
310     if (mknod(path.c_str(), mode, dev) && (errno == EEXIST) && !secontext.empty()) {
311         char* fcon = nullptr;
312         int rc = lgetfilecon(path.c_str(), &fcon);
313         if (rc < 0) {
314             PLOG(ERROR) << "Cannot get SELinux label on '" << path << "' device";
315             goto out;
316         }
317 
318         bool different = fcon != secontext;
319         freecon(fcon);
320 
321         if (different && lsetfilecon(path.c_str(), secontext.c_str())) {
322             PLOG(ERROR) << "Cannot set '" << secontext << "' SELinux label on '" << path
323                         << "' device";
324         }
325 
326         struct stat s;
327         if (stat(path.c_str(), &s) == 0) {
328             if (gid != s.st_gid) {
329                 new_group = gid;
330             }
331         if (mode != s.st_mode) {
332             if (chmod(path.c_str(), mode) != 0) {
333                 PLOG(ERROR) << "Cannot chmod " << path << " to " << mode;
334             }
335         }
336         } else {
337             PLOG(ERROR) << "Cannot stat " << path;
338         }
339     }
340 
341 out:
342     if (chown(path.c_str(), uid, new_group) < 0) {
343         PLOG(ERROR) << "Cannot chown " << path << " " << uid << " " << new_group;
344     }
345     if (setegid(AID_ROOT)) {
346         PLOG(FATAL) << "setegid(AID_ROOT) failed";
347     }
348 
349     if (!secontext.empty()) {
350         setfscreatecon(nullptr);
351     }
352 }
353 
354 // replaces any unacceptable characters with '_', the
355 // length of the resulting string is equal to the input string
SanitizePartitionName(std::string * string)356 void SanitizePartitionName(std::string* string) {
357     const char* accept =
358         "abcdefghijklmnopqrstuvwxyz"
359         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
360         "0123456789"
361         "_-.";
362 
363     if (!string) return;
364 
365     std::string::size_type pos = 0;
366     while ((pos = string->find_first_not_of(accept, pos)) != std::string::npos) {
367         (*string)[pos] = '_';
368     }
369 }
370 
GetBlockDeviceSymlinks(const Uevent & uevent) const371 std::vector<std::string> DeviceHandler::GetBlockDeviceSymlinks(const Uevent& uevent) const {
372     std::string device;
373     std::string type;
374     std::string partition;
375     std::string uuid;
376 
377     if (FindPlatformDevice(uevent.path, &device)) {
378         // Skip /devices/platform or /devices/ if present
379         static const std::string devices_platform_prefix = "/devices/platform/";
380         static const std::string devices_prefix = "/devices/";
381 
382         if (StartsWith(device, devices_platform_prefix)) {
383             device = device.substr(devices_platform_prefix.length());
384         } else if (StartsWith(device, devices_prefix)) {
385             device = device.substr(devices_prefix.length());
386         }
387 
388         type = "platform";
389     } else if (FindPciDevicePrefix(uevent.path, &device)) {
390         type = "pci";
391     } else if (FindVbdDevicePrefix(uevent.path, &device)) {
392         type = "vbd";
393     } else if (FindDmDevice(uevent, &partition, &uuid)) {
394         std::vector<std::string> symlinks = {"/dev/block/mapper/" + partition};
395         if (!uuid.empty()) {
396             symlinks.emplace_back("/dev/block/mapper/by-uuid/" + uuid);
397         }
398         return symlinks;
399     } else {
400         return {};
401     }
402 
403     std::vector<std::string> links;
404 
405     LOG(VERBOSE) << "found " << type << " device " << device;
406 
407     auto link_path = "/dev/block/" + type + "/" + device;
408 
409     bool is_boot_device = boot_devices_.find(device) != boot_devices_.end();
410     if (!uevent.partition_name.empty()) {
411         std::string partition_name_sanitized(uevent.partition_name);
412         SanitizePartitionName(&partition_name_sanitized);
413         if (partition_name_sanitized != uevent.partition_name) {
414             LOG(VERBOSE) << "Linking partition '" << uevent.partition_name << "' as '"
415                          << partition_name_sanitized << "'";
416         }
417         links.emplace_back(link_path + "/by-name/" + partition_name_sanitized);
418         // Adds symlink: /dev/block/by-name/<partition_name>.
419         if (is_boot_device) {
420             links.emplace_back("/dev/block/by-name/" + partition_name_sanitized);
421         }
422     } else if (is_boot_device) {
423         // If we don't have a partition name but we are a partition on a boot device, create a
424         // symlink of /dev/block/by-name/<device_name> for symmetry.
425         links.emplace_back("/dev/block/by-name/" + uevent.device_name);
426         auto partition_name = GetPartitionNameForDevice(uevent.device_name);
427         if (!partition_name.empty()) {
428             links.emplace_back("/dev/block/by-name/" + partition_name);
429         }
430     }
431 
432     std::string model;
433     if (ReadFileToString("/sys/class/block/" + uevent.device_name + "/queue/zoned", &model) &&
434         !StartsWith(model, "none")) {
435         links.emplace_back("/dev/block/by-name/zoned_device");
436     }
437 
438     auto last_slash = uevent.path.rfind('/');
439     links.emplace_back(link_path + "/" + uevent.path.substr(last_slash + 1));
440 
441     return links;
442 }
443 
RemoveDeviceMapperLinks(const std::string & devpath)444 static void RemoveDeviceMapperLinks(const std::string& devpath) {
445     std::vector<std::string> dirs = {
446             "/dev/block/mapper",
447             "/dev/block/mapper/by-uuid",
448     };
449     for (const auto& dir : dirs) {
450         if (access(dir.c_str(), F_OK) != 0) continue;
451 
452         std::unique_ptr<DIR, decltype(&closedir)> dh(opendir(dir.c_str()), closedir);
453         if (!dh) {
454             PLOG(ERROR) << "Failed to open directory " << dir;
455             continue;
456         }
457 
458         struct dirent* dp;
459         std::string link_path;
460         while ((dp = readdir(dh.get())) != nullptr) {
461             if (dp->d_type != DT_LNK) continue;
462 
463             auto path = dir + "/" + dp->d_name;
464             if (Readlink(path, &link_path) && link_path == devpath) {
465                 unlink(path.c_str());
466             }
467         }
468     }
469 }
470 
HandleDevice(const std::string & action,const std::string & devpath,bool block,int major,int minor,const std::vector<std::string> & links) const471 void DeviceHandler::HandleDevice(const std::string& action, const std::string& devpath, bool block,
472                                  int major, int minor, const std::vector<std::string>& links) const {
473     if (action == "add") {
474         MakeDevice(devpath, block, major, minor, links);
475     }
476 
477     // Handle device-mapper nodes.
478     // On kernels <= 5.10, the "add" event is fired on DM_DEV_CREATE, but does not contain name
479     // information until DM_TABLE_LOAD - thus, we wait for a "change" event.
480     // On kernels >= 5.15, the "add" event is fired on DM_TABLE_LOAD, followed by a "change"
481     // event.
482     if (action == "add" || (action == "change" && StartsWith(devpath, "/dev/block/dm-"))) {
483         for (const auto& link : links) {
484             if (!mkdir_recursive(Dirname(link), 0755)) {
485                 PLOG(ERROR) << "Failed to create directory " << Dirname(link);
486             }
487 
488             if (symlink(devpath.c_str(), link.c_str())) {
489                 if (errno != EEXIST) {
490                     PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link;
491                 } else if (std::string link_path;
492                            Readlink(link, &link_path) && link_path != devpath) {
493                     PLOG(ERROR) << "Failed to symlink " << devpath << " to " << link
494                                 << ", which already links to: " << link_path;
495                 }
496             }
497         }
498     }
499 
500     if (action == "remove") {
501         if (StartsWith(devpath, "/dev/block/dm-")) {
502             RemoveDeviceMapperLinks(devpath);
503         }
504         for (const auto& link : links) {
505             std::string link_path;
506             if (Readlink(link, &link_path) && link_path == devpath) {
507                 unlink(link.c_str());
508             }
509         }
510         unlink(devpath.c_str());
511     }
512 }
513 
HandleAshmemUevent(const Uevent & uevent)514 void DeviceHandler::HandleAshmemUevent(const Uevent& uevent) {
515     if (uevent.device_name == "ashmem") {
516         static const std::string boot_id_path = "/proc/sys/kernel/random/boot_id";
517         std::string boot_id;
518         if (!ReadFileToString(boot_id_path, &boot_id)) {
519             PLOG(ERROR) << "Cannot duplicate ashmem device node. Failed to read " << boot_id_path;
520             return;
521         };
522         boot_id = Trim(boot_id);
523 
524         Uevent dup_ashmem_uevent = uevent;
525         dup_ashmem_uevent.device_name += boot_id;
526         dup_ashmem_uevent.path += boot_id;
527         HandleUevent(dup_ashmem_uevent);
528     }
529 }
530 
HandleUevent(const Uevent & uevent)531 void DeviceHandler::HandleUevent(const Uevent& uevent) {
532   if (uevent.action == "add" || uevent.action == "change" ||
533       uevent.action == "bind" || uevent.action == "online") {
534     FixupSysPermissions(uevent.path, uevent.subsystem);
535   }
536 
537     // if it's not a /dev device, nothing to do
538     if (uevent.major < 0 || uevent.minor < 0) return;
539 
540     std::string devpath;
541     std::vector<std::string> links;
542     bool block = false;
543 
544     if (uevent.subsystem == "block") {
545         block = true;
546         devpath = "/dev/block/" + Basename(uevent.path);
547 
548         if (StartsWith(uevent.path, "/devices")) {
549             links = GetBlockDeviceSymlinks(uevent);
550         }
551     } else if (const auto subsystem =
552                    std::find(subsystems_.cbegin(), subsystems_.cend(), uevent.subsystem);
553                subsystem != subsystems_.cend()) {
554         devpath = subsystem->ParseDevPath(uevent);
555     } else if (uevent.subsystem == "usb") {
556         if (!uevent.device_name.empty()) {
557             devpath = "/dev/" + uevent.device_name;
558         } else {
559             // This imitates the file system that would be created
560             // if we were using devfs instead.
561             // Minors are broken up into groups of 128, starting at "001"
562             int bus_id = uevent.minor / 128 + 1;
563             int device_id = uevent.minor % 128 + 1;
564             devpath = StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);
565         }
566     } else if (StartsWith(uevent.subsystem, "usb")) {
567         // ignore other USB events
568         return;
569     } else if (uevent.subsystem == "misc" && StartsWith(uevent.device_name, "dm-user/")) {
570         devpath = "/dev/dm-user/" + uevent.device_name.substr(8);
571     } else {
572         devpath = "/dev/" + Basename(uevent.path);
573     }
574 
575     mkdir_recursive(Dirname(devpath), 0755);
576 
577     HandleDevice(uevent.action, devpath, block, uevent.major, uevent.minor, links);
578 
579     // Duplicate /dev/ashmem device and name it /dev/ashmem<boot_id>.
580     // TODO(b/111903542): remove once all users of /dev/ashmem are migrated to libcutils API.
581     HandleAshmemUevent(uevent);
582 }
583 
ColdbootDone()584 void DeviceHandler::ColdbootDone() {
585     skip_restorecon_ = false;
586 }
587 
DeviceHandler(std::vector<Permissions> dev_permissions,std::vector<SysfsPermissions> sysfs_permissions,std::vector<Subsystem> subsystems,std::set<std::string> boot_devices,bool skip_restorecon)588 DeviceHandler::DeviceHandler(std::vector<Permissions> dev_permissions,
589                              std::vector<SysfsPermissions> sysfs_permissions,
590                              std::vector<Subsystem> subsystems, std::set<std::string> boot_devices,
591                              bool skip_restorecon)
592     : dev_permissions_(std::move(dev_permissions)),
593       sysfs_permissions_(std::move(sysfs_permissions)),
594       subsystems_(std::move(subsystems)),
595       boot_devices_(std::move(boot_devices)),
596       skip_restorecon_(skip_restorecon),
597       sysfs_mount_point_("/sys") {}
598 
DeviceHandler()599 DeviceHandler::DeviceHandler()
600     : DeviceHandler(std::vector<Permissions>{}, std::vector<SysfsPermissions>{},
601                     std::vector<Subsystem>{}, std::set<std::string>{}, false) {}
602 
603 }  // namespace init
604 }  // namespace android
605