1 /* ***** BEGIN LICENSE BLOCK *****
2 * Copyright (C) 2012-2016, Peter Hatina <phatina@gmail.com>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 * ***** END LICENSE BLOCK ***** */
17
18 #include <config.h>
19 #include <cstdio>
20 #include <cstring>
21 #ifdef HAVE_LIBUSB1
22 # include <iomanip>
23 # include <sstream>
24 #endif // HAVE_LIBUSB1
25 extern "C" {
26 # include <dirent.h>
27 # include <libgen.h>
28 # include <limits.h>
29 # include <stdlib.h>
30 # include <sys/stat.h>
31 # include <sys/types.h>
32 # include <unistd.h>
33 }
34 #ifdef HAVE_LIBUSB1
35 # include <climits>
36 extern "C" {
37 # include <libmtp.h>
38 # include <libusb.h>
39 }
40 #endif // HAVE_LIBUSB1
41 #include "simple-mtpfs-log.h"
42 #include "simple-mtpfs-util.h"
43
44 const std::string devnull = "/dev/null";
45
46 bool StreamHelper::s_enabled = false;
47 int StreamHelper::s_stdout = -1;
48 int StreamHelper::s_stderr = -1;
49
on()50 void StreamHelper::on()
51 {
52 if (!s_enabled)
53 return;
54
55 freopen(devnull.c_str(), "w", stdout);
56 freopen(devnull.c_str(), "w", stderr);
57 dup2(s_stdout, fileno(stdout));
58 dup2(s_stderr, fileno(stderr));
59 close(s_stdout);
60 close(s_stderr);
61 setvbuf(stdout, NULL, _IOLBF, 0);
62 setvbuf(stderr, NULL, _IOLBF, 0);
63
64 s_enabled = false;
65 }
66
off()67 void StreamHelper::off()
68 {
69 if (s_enabled)
70 return;
71
72 fflush(stdout);
73 fflush(stderr);
74 s_stdout = dup(fileno(stdout));
75 s_stderr = dup(fileno(stderr));
76 freopen(devnull.c_str(), "w", stdout);
77 freopen(devnull.c_str(), "w", stderr);
78
79 s_enabled = true;
80 }
81
smtpfs_dirname(const std::string & path)82 std::string smtpfs_dirname(const std::string &path)
83 {
84 char *str = strdup(path.c_str());
85 std::string result(dirname(str));
86 free(static_cast<void*>(str));
87 return result;
88 }
89
smtpfs_basename(const std::string & path)90 std::string smtpfs_basename(const std::string &path)
91 {
92 char *str = strdup(path.c_str());
93 std::string result(basename(str));
94 free(static_cast<void*>(str));
95 return result;
96 }
97
smtpfs_realpath(const std::string & path)98 std::string smtpfs_realpath(const std::string &path)
99 {
100 char buf[PATH_MAX + 1];
101 char *real_path = realpath(path.c_str(), buf);
102 return std::string(real_path ? buf : "");
103 }
104
smtpfs_get_tmpdir()105 std::string smtpfs_get_tmpdir()
106 {
107 const char *c_tmp = getenv("TMP");
108 std::string tmp_dir;
109 if (c_tmp) {
110 tmp_dir = smtpfs_realpath(c_tmp);
111 } else {
112 c_tmp = getenv("TMPDIR");
113 if (!c_tmp)
114 c_tmp = TMPDIR;
115 tmp_dir = smtpfs_realpath(c_tmp);
116 }
117
118 tmp_dir += "/simple-mtpfs-XXXXXX";
119 char *c_tmp_dir = ::mkdtemp(::strdup(tmp_dir.c_str()));
120
121 tmp_dir.assign(c_tmp_dir);
122 ::free(static_cast<void*>(c_tmp_dir));
123
124 return tmp_dir;
125 }
126
smtpfs_create_dir(const std::string & dirname)127 bool smtpfs_create_dir(const std::string &dirname)
128 {
129 return ::mkdir(dirname.c_str(), S_IRWXU) == 0;
130 }
131
smtpfs_remove_dir(const std::string & dirname)132 bool smtpfs_remove_dir(const std::string &dirname)
133 {
134 DIR *dir;
135 struct dirent *entry;
136 std::string path;
137
138 dir = ::opendir(dirname.c_str());
139 if (!dir)
140 return false;
141
142 while ((entry = ::readdir(dir))) {
143 if (strcmp(entry->d_name, ".") && strcmp(entry->d_name, "..")) {
144 path = dirname + "/" + entry->d_name;
145 if (entry->d_type == DT_DIR)
146 return smtpfs_remove_dir(path);
147 ::unlink(path.c_str());
148 }
149 }
150 ::closedir(dir);
151 ::remove(dirname.c_str());
152 return true;
153 }
154
smtpfs_usb_devpath(const std::string & path,uint8_t * bnum,uint8_t * dnum)155 bool smtpfs_usb_devpath(const std::string &path, uint8_t *bnum, uint8_t *dnum)
156 {
157 unsigned int bus, dev;
158 #ifdef USB_DEVPATH
159 std::string realpath(smtpfs_realpath(path));
160 if (realpath.empty() ||
161 sscanf(realpath.c_str(), USB_DEVPATH, &bus, &dev) != 2)
162 #endif
163 if (sscanf(path.c_str(), "%u/%u", &bus, &dev) != 2)
164 return false;
165
166 if (bus > 255 || dev > 255)
167 return false;
168
169 *bnum = bus;
170 *dnum = dev;
171 return true;
172 }
173
174 #ifdef HAVE_LIBUSB1
smtpfs_raw_device_new_priv(libusb_device * usb_device)175 LIBMTP_raw_device_t *smtpfs_raw_device_new_priv(libusb_device *usb_device)
176 {
177 if (!usb_device)
178 return nullptr;
179
180 LIBMTP_raw_device_t *device = static_cast<LIBMTP_raw_device_t*>(
181 malloc(sizeof(LIBMTP_raw_device_t)));
182
183 if (!device)
184 return nullptr;
185
186 struct libusb_device_descriptor desc;
187 int err = libusb_get_device_descriptor(usb_device, &desc);
188 if (err != LIBUSB_SUCCESS) {
189 free(static_cast<void*>(device));
190 return nullptr;
191 }
192
193 device->device_entry.vendor = nullptr; // TODO: vendor string
194 device->device_entry.vendor_id = desc.idVendor;
195 device->device_entry.product = nullptr; // TODO: product string
196 device->device_entry.product_id = desc.idProduct;
197 device->device_entry.device_flags = 0;
198
199 device->bus_location = static_cast<uint32_t>(libusb_get_bus_number(usb_device));
200 device->devnum = libusb_get_device_address(usb_device);
201
202 return device;
203 }
204
smtpfs_raw_device_new(const std::string & path)205 LIBMTP_raw_device_t *smtpfs_raw_device_new(const std::string &path)
206 {
207 uint8_t bnum, dnum;
208 if (!smtpfs_usb_devpath(path, &bnum, &dnum))
209 return nullptr;
210
211 if (libusb_init(NULL) != 0)
212 return nullptr;
213
214 libusb_device **dev_list;
215 ssize_t num_devs = libusb_get_device_list(NULL, &dev_list);
216 if (!num_devs) {
217 libusb_exit(NULL);
218 return nullptr;
219 }
220
221 libusb_device *dev = nullptr;
222 for (auto i = 0; i < num_devs; ++i) {
223 dev = dev_list[i];
224 if (bnum == libusb_get_bus_number(dev_list[i]) &&
225 dnum == libusb_get_device_address(dev_list[i]))
226 break;
227 dev = nullptr;
228 }
229
230 LIBMTP_raw_device_t *raw_device = smtpfs_raw_device_new_priv(dev);
231
232 libusb_free_device_list(dev_list, 0);
233 libusb_exit(NULL);
234
235 return raw_device;
236 }
237
smtpfs_reset_device(LIBMTP_raw_device_t * device)238 bool smtpfs_reset_device(LIBMTP_raw_device_t *device)
239 {
240 if (libusb_init(NULL) != 0)
241 return false;
242
243 libusb_device **dev_list;
244 ssize_t num_devs = libusb_get_device_list(NULL, &dev_list);
245 if (!num_devs) {
246 libusb_exit(NULL);
247 return false;
248 }
249
250 libusb_device_handle *dev_handle = nullptr;
251 for (auto i = 0; i < num_devs; ++i) {
252 uint8_t bnum = libusb_get_bus_number(dev_list[i]);
253 uint8_t dnum = libusb_get_device_address(dev_list[i]);
254
255 if (static_cast<uint32_t>(bnum) == device->bus_location &&
256 dnum == device->devnum)
257 {
258 libusb_open(dev_list[i], &dev_handle);
259 libusb_reset_device(dev_handle);
260 libusb_close(dev_handle);
261 break;
262 }
263 }
264
265 libusb_free_device_list(dev_list, 0);
266 libusb_exit(NULL);
267
268 return true;
269 }
270
smtpfs_raw_device_free(LIBMTP_raw_device_t * device)271 void smtpfs_raw_device_free(LIBMTP_raw_device_t *device)
272 {
273 if (!device)
274 return;
275
276 free(static_cast<void*>(device->device_entry.vendor));
277 free(static_cast<void*>(device->device_entry.product));
278 free(static_cast<void*>(device));
279 }
280 #endif // HAVE_LIBUSB1
281
smtpfs_check_dir(const std::string & path)282 bool smtpfs_check_dir(const std::string &path)
283 {
284 struct stat buf;
285 if (::stat(path.c_str(), &buf) == 0 && S_ISDIR(buf.st_mode)
286 && ::access(path.c_str(), R_OK | W_OK | X_OK) == 0)
287 {
288 return true;
289 }
290
291 return false;
292 }
293