1/*
2 * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24#include "precompiled.hpp"
25#include "gc/z/zArray.inline.hpp"
26#include "gc/z/zBackingPath_linux_x86.hpp"
27#include "gc/z/zErrno.hpp"
28#include "logging/log.hpp"
29
30#include <stdio.h>
31#include <unistd.h>
32
33// Mount information, see proc(5) for more details.
34#define PROC_SELF_MOUNTINFO "/proc/self/mountinfo"
35
36ZBackingPath::ZBackingPath(const char* filesystem, const char** preferred_mountpoints) {
37 if (ZPath != NULL) {
38 // Use specified path
39 _path = strdup(ZPath);
40 } else {
41 // Find suitable path
42 _path = find_mountpoint(filesystem, preferred_mountpoints);
43 }
44}
45
46ZBackingPath::~ZBackingPath() {
47 free(_path);
48 _path = NULL;
49}
50
51char* ZBackingPath::get_mountpoint(const char* line, const char* filesystem) const {
52 char* line_mountpoint = NULL;
53 char* line_filesystem = NULL;
54
55 // Parse line and return a newly allocated string containing the mount point if
56 // the line contains a matching filesystem and the mount point is accessible by
57 // the current user.
58 if (sscanf(line, "%*u %*u %*u:%*u %*s %ms %*[^-]- %ms", &line_mountpoint, &line_filesystem) != 2 ||
59 strcmp(line_filesystem, filesystem) != 0 ||
60 access(line_mountpoint, R_OK|W_OK|X_OK) != 0) {
61 // Not a matching or accessible filesystem
62 free(line_mountpoint);
63 line_mountpoint = NULL;
64 }
65
66 free(line_filesystem);
67
68 return line_mountpoint;
69}
70
71void ZBackingPath::get_mountpoints(const char* filesystem, ZArray<char*>* mountpoints) const {
72 FILE* fd = fopen(PROC_SELF_MOUNTINFO, "r");
73 if (fd == NULL) {
74 ZErrno err;
75 log_error(gc)("Failed to open %s: %s", PROC_SELF_MOUNTINFO, err.to_string());
76 return;
77 }
78
79 char* line = NULL;
80 size_t length = 0;
81
82 while (getline(&line, &length, fd) != -1) {
83 char* const mountpoint = get_mountpoint(line, filesystem);
84 if (mountpoint != NULL) {
85 mountpoints->add(mountpoint);
86 }
87 }
88
89 free(line);
90 fclose(fd);
91}
92
93void ZBackingPath::free_mountpoints(ZArray<char*>* mountpoints) const {
94 ZArrayIterator<char*> iter(mountpoints);
95 for (char* mountpoint; iter.next(&mountpoint);) {
96 free(mountpoint);
97 }
98 mountpoints->clear();
99}
100
101char* ZBackingPath::find_preferred_mountpoint(const char* filesystem,
102 ZArray<char*>* mountpoints,
103 const char** preferred_mountpoints) const {
104 // Find preferred mount point
105 ZArrayIterator<char*> iter1(mountpoints);
106 for (char* mountpoint; iter1.next(&mountpoint);) {
107 for (const char** preferred = preferred_mountpoints; *preferred != NULL; preferred++) {
108 if (!strcmp(mountpoint, *preferred)) {
109 // Preferred mount point found
110 return strdup(mountpoint);
111 }
112 }
113 }
114
115 // Preferred mount point not found
116 log_error(gc)("More than one %s filesystem found:", filesystem);
117 ZArrayIterator<char*> iter2(mountpoints);
118 for (char* mountpoint; iter2.next(&mountpoint);) {
119 log_error(gc)(" %s", mountpoint);
120 }
121
122 return NULL;
123}
124
125char* ZBackingPath::find_mountpoint(const char* filesystem, const char** preferred_mountpoints) const {
126 char* path = NULL;
127 ZArray<char*> mountpoints;
128
129 get_mountpoints(filesystem, &mountpoints);
130
131 if (mountpoints.size() == 0) {
132 // No mount point found
133 log_error(gc)("Failed to find an accessible %s filesystem", filesystem);
134 } else if (mountpoints.size() == 1) {
135 // One mount point found
136 path = strdup(mountpoints.at(0));
137 } else {
138 // More than one mount point found
139 path = find_preferred_mountpoint(filesystem, &mountpoints, preferred_mountpoints);
140 }
141
142 free_mountpoints(&mountpoints);
143
144 return path;
145}
146
147const char* ZBackingPath::get() const {
148 return _path;
149}
150