Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
6147 | serge | 1 | /* |
2 | * Copyright (c) 2014 Lukasz Marek |
||
3 | * |
||
4 | * This file is part of FFmpeg. |
||
5 | * |
||
6 | * FFmpeg is free software; you can redistribute it and/or |
||
7 | * modify it under the terms of the GNU Lesser General Public |
||
8 | * License as published by the Free Software Foundation; either |
||
9 | * version 2.1 of the License, or (at your option) any later version. |
||
10 | * |
||
11 | * FFmpeg is distributed in the hope that it will be useful, |
||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||
14 | * Lesser General Public License for more details. |
||
15 | * |
||
16 | * You should have received a copy of the GNU Lesser General Public |
||
17 | * License along with FFmpeg; if not, write to the Free Software |
||
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||
19 | */ |
||
20 | |||
21 | #include |
||
22 | #include "libavutil/avstring.h" |
||
23 | #include "libavutil/opt.h" |
||
24 | #include "avformat.h" |
||
25 | #include "internal.h" |
||
26 | #include "url.h" |
||
27 | |||
28 | typedef struct { |
||
29 | const AVClass *class; |
||
30 | SMBCCTX *ctx; |
||
31 | int dh; |
||
32 | int fd; |
||
33 | int64_t filesize; |
||
34 | int trunc; |
||
35 | int timeout; |
||
36 | char *workgroup; |
||
37 | } LIBSMBContext; |
||
38 | |||
39 | static void libsmbc_get_auth_data(SMBCCTX *c, const char *server, const char *share, |
||
40 | char *workgroup, int workgroup_len, |
||
41 | char *username, int username_len, |
||
42 | char *password, int password_len) |
||
43 | { |
||
44 | /* Do nothing yet. Credentials are passed via url. |
||
45 | * Callback must exists, there might be a segmentation fault otherwise. */ |
||
46 | } |
||
47 | |||
48 | static av_cold int libsmbc_connect(URLContext *h) |
||
49 | { |
||
50 | LIBSMBContext *libsmbc = h->priv_data; |
||
51 | |||
52 | libsmbc->ctx = smbc_new_context(); |
||
53 | if (!libsmbc->ctx) { |
||
54 | int ret = AVERROR(errno); |
||
55 | av_log(h, AV_LOG_ERROR, "Cannot create context: %s.\n", strerror(errno)); |
||
56 | return ret; |
||
57 | } |
||
58 | if (!smbc_init_context(libsmbc->ctx)) { |
||
59 | int ret = AVERROR(errno); |
||
60 | av_log(h, AV_LOG_ERROR, "Cannot initialize context: %s.\n", strerror(errno)); |
||
61 | return ret; |
||
62 | } |
||
63 | smbc_set_context(libsmbc->ctx); |
||
64 | |||
65 | smbc_setOptionUserData(libsmbc->ctx, h); |
||
66 | smbc_setFunctionAuthDataWithContext(libsmbc->ctx, libsmbc_get_auth_data); |
||
67 | |||
68 | if (libsmbc->timeout != -1) |
||
69 | smbc_setTimeout(libsmbc->ctx, libsmbc->timeout); |
||
70 | if (libsmbc->workgroup) |
||
71 | smbc_setWorkgroup(libsmbc->ctx, libsmbc->workgroup); |
||
72 | |||
73 | if (smbc_init(NULL, 0) < 0) { |
||
74 | int ret = AVERROR(errno); |
||
75 | av_log(h, AV_LOG_ERROR, "Initialization failed: %s\n", strerror(errno)); |
||
76 | return ret; |
||
77 | } |
||
78 | return 0; |
||
79 | } |
||
80 | |||
81 | static av_cold int libsmbc_close(URLContext *h) |
||
82 | { |
||
83 | LIBSMBContext *libsmbc = h->priv_data; |
||
84 | if (libsmbc->fd >= 0) { |
||
85 | smbc_close(libsmbc->fd); |
||
86 | libsmbc->fd = -1; |
||
87 | } |
||
88 | if (libsmbc->ctx) { |
||
89 | smbc_free_context(libsmbc->ctx, 1); |
||
90 | libsmbc->ctx = NULL; |
||
91 | } |
||
92 | return 0; |
||
93 | } |
||
94 | |||
95 | static av_cold int libsmbc_open(URLContext *h, const char *url, int flags) |
||
96 | { |
||
97 | LIBSMBContext *libsmbc = h->priv_data; |
||
98 | int access, ret; |
||
99 | struct stat st; |
||
100 | |||
101 | libsmbc->fd = -1; |
||
102 | libsmbc->filesize = -1; |
||
103 | |||
104 | if ((ret = libsmbc_connect(h)) < 0) |
||
105 | goto fail; |
||
106 | |||
107 | if ((flags & AVIO_FLAG_WRITE) && (flags & AVIO_FLAG_READ)) { |
||
108 | access = O_CREAT | O_RDWR; |
||
109 | if (libsmbc->trunc) |
||
110 | access |= O_TRUNC; |
||
111 | } else if (flags & AVIO_FLAG_WRITE) { |
||
112 | access = O_CREAT | O_WRONLY; |
||
113 | if (libsmbc->trunc) |
||
114 | access |= O_TRUNC; |
||
115 | } else |
||
116 | access = O_RDONLY; |
||
117 | |||
118 | /* 0666 = -rw-rw-rw- = read+write for everyone, minus umask */ |
||
119 | if ((libsmbc->fd = smbc_open(url, access, 0666)) < 0) { |
||
120 | ret = AVERROR(errno); |
||
121 | av_log(h, AV_LOG_ERROR, "File open failed: %s\n", strerror(errno)); |
||
122 | goto fail; |
||
123 | } |
||
124 | |||
125 | if (smbc_fstat(libsmbc->fd, &st) < 0) |
||
126 | av_log(h, AV_LOG_WARNING, "Cannot stat file: %s\n", strerror(errno)); |
||
127 | else |
||
128 | libsmbc->filesize = st.st_size; |
||
129 | |||
130 | return 0; |
||
131 | fail: |
||
132 | libsmbc_close(h); |
||
133 | return ret; |
||
134 | } |
||
135 | |||
136 | static int64_t libsmbc_seek(URLContext *h, int64_t pos, int whence) |
||
137 | { |
||
138 | LIBSMBContext *libsmbc = h->priv_data; |
||
139 | int64_t newpos; |
||
140 | |||
141 | if (whence == AVSEEK_SIZE) { |
||
142 | if (libsmbc->filesize == -1) { |
||
143 | av_log(h, AV_LOG_ERROR, "Error during seeking: filesize is unknown.\n"); |
||
144 | return AVERROR(EIO); |
||
145 | } else |
||
146 | return libsmbc->filesize; |
||
147 | } |
||
148 | |||
149 | if ((newpos = smbc_lseek(libsmbc->fd, pos, whence)) < 0) { |
||
150 | int err = errno; |
||
151 | av_log(h, AV_LOG_ERROR, "Error during seeking: %s\n", strerror(err)); |
||
152 | return AVERROR(err); |
||
153 | } |
||
154 | |||
155 | return newpos; |
||
156 | } |
||
157 | |||
158 | static int libsmbc_read(URLContext *h, unsigned char *buf, int size) |
||
159 | { |
||
160 | LIBSMBContext *libsmbc = h->priv_data; |
||
161 | int bytes_read; |
||
162 | |||
163 | if ((bytes_read = smbc_read(libsmbc->fd, buf, size)) < 0) { |
||
164 | int ret = AVERROR(errno); |
||
165 | av_log(h, AV_LOG_ERROR, "Read error: %s\n", strerror(errno)); |
||
166 | return ret; |
||
167 | } |
||
168 | |||
169 | return bytes_read; |
||
170 | } |
||
171 | |||
172 | static int libsmbc_write(URLContext *h, const unsigned char *buf, int size) |
||
173 | { |
||
174 | LIBSMBContext *libsmbc = h->priv_data; |
||
175 | int bytes_written; |
||
176 | |||
177 | if ((bytes_written = smbc_write(libsmbc->fd, buf, size)) < 0) { |
||
178 | int ret = AVERROR(errno); |
||
179 | av_log(h, AV_LOG_ERROR, "Write error: %s\n", strerror(errno)); |
||
180 | return ret; |
||
181 | } |
||
182 | |||
183 | return bytes_written; |
||
184 | } |
||
185 | |||
186 | static int libsmbc_open_dir(URLContext *h) |
||
187 | { |
||
188 | LIBSMBContext *libsmbc = h->priv_data; |
||
189 | int ret; |
||
190 | |||
191 | if ((ret = libsmbc_connect(h)) < 0) |
||
192 | goto fail; |
||
193 | |||
194 | if ((libsmbc->dh = smbc_opendir(h->filename)) < 0) { |
||
195 | ret = AVERROR(errno); |
||
196 | av_log(h, AV_LOG_ERROR, "Error opening dir: %s\n", strerror(errno)); |
||
197 | goto fail; |
||
198 | } |
||
199 | |||
200 | return 0; |
||
201 | |||
202 | fail: |
||
203 | libsmbc_close(h); |
||
204 | return ret; |
||
205 | } |
||
206 | |||
207 | static int libsmbc_read_dir(URLContext *h, AVIODirEntry **next) |
||
208 | { |
||
209 | LIBSMBContext *libsmbc = h->priv_data; |
||
210 | AVIODirEntry *entry; |
||
211 | struct smbc_dirent *dirent = NULL; |
||
212 | char *url = NULL; |
||
213 | int skip_entry; |
||
214 | |||
215 | *next = entry = ff_alloc_dir_entry(); |
||
216 | if (!entry) |
||
217 | return AVERROR(ENOMEM); |
||
218 | |||
219 | do { |
||
220 | skip_entry = 0; |
||
221 | dirent = smbc_readdir(libsmbc->dh); |
||
222 | if (!dirent) { |
||
223 | av_freep(next); |
||
224 | return 0; |
||
225 | } |
||
226 | switch (dirent->smbc_type) { |
||
227 | case SMBC_DIR: |
||
228 | entry->type = AVIO_ENTRY_DIRECTORY; |
||
229 | break; |
||
230 | case SMBC_FILE: |
||
231 | entry->type = AVIO_ENTRY_FILE; |
||
232 | break; |
||
233 | case SMBC_FILE_SHARE: |
||
234 | entry->type = AVIO_ENTRY_SHARE; |
||
235 | break; |
||
236 | case SMBC_SERVER: |
||
237 | entry->type = AVIO_ENTRY_SERVER; |
||
238 | break; |
||
239 | case SMBC_WORKGROUP: |
||
240 | entry->type = AVIO_ENTRY_WORKGROUP; |
||
241 | break; |
||
242 | case SMBC_COMMS_SHARE: |
||
243 | case SMBC_IPC_SHARE: |
||
244 | case SMBC_PRINTER_SHARE: |
||
245 | skip_entry = 1; |
||
246 | break; |
||
247 | case SMBC_LINK: |
||
248 | default: |
||
249 | entry->type = AVIO_ENTRY_UNKNOWN; |
||
250 | break; |
||
251 | } |
||
252 | } while (skip_entry || !strcmp(dirent->name, ".") || |
||
253 | !strcmp(dirent->name, "..")); |
||
254 | |||
255 | entry->name = av_strdup(dirent->name); |
||
256 | if (!entry->name) { |
||
257 | av_freep(next); |
||
258 | return AVERROR(ENOMEM); |
||
259 | } |
||
260 | |||
261 | url = av_append_path_component(h->filename, dirent->name); |
||
262 | if (url) { |
||
263 | struct stat st; |
||
264 | if (!smbc_stat(url, &st)) { |
||
265 | entry->group_id = st.st_gid; |
||
266 | entry->user_id = st.st_uid; |
||
267 | entry->size = st.st_size; |
||
268 | entry->filemode = st.st_mode & 0777; |
||
269 | entry->modification_timestamp = INT64_C(1000000) * st.st_mtime; |
||
270 | entry->access_timestamp = INT64_C(1000000) * st.st_atime; |
||
271 | entry->status_change_timestamp = INT64_C(1000000) * st.st_ctime; |
||
272 | } |
||
273 | av_free(url); |
||
274 | } |
||
275 | |||
276 | return 0; |
||
277 | } |
||
278 | |||
279 | static int libsmbc_close_dir(URLContext *h) |
||
280 | { |
||
281 | LIBSMBContext *libsmbc = h->priv_data; |
||
282 | if (libsmbc->dh >= 0) { |
||
283 | smbc_closedir(libsmbc->dh); |
||
284 | libsmbc->dh = -1; |
||
285 | } |
||
286 | libsmbc_close(h); |
||
287 | return 0; |
||
288 | } |
||
289 | |||
290 | static int libsmbc_delete(URLContext *h) |
||
291 | { |
||
292 | LIBSMBContext *libsmbc = h->priv_data; |
||
293 | int ret; |
||
294 | struct stat st; |
||
295 | |||
296 | if ((ret = libsmbc_connect(h)) < 0) |
||
297 | goto cleanup; |
||
298 | |||
299 | if ((libsmbc->fd = smbc_open(h->filename, O_WRONLY, 0666)) < 0) { |
||
300 | ret = AVERROR(errno); |
||
301 | goto cleanup; |
||
302 | } |
||
303 | |||
304 | if (smbc_fstat(libsmbc->fd, &st) < 0) { |
||
305 | ret = AVERROR(errno); |
||
306 | goto cleanup; |
||
307 | } |
||
308 | |||
309 | smbc_close(libsmbc->fd); |
||
310 | libsmbc->fd = -1; |
||
311 | |||
312 | if (S_ISDIR(st.st_mode)) { |
||
313 | if (smbc_rmdir(h->filename) < 0) { |
||
314 | ret = AVERROR(errno); |
||
315 | goto cleanup; |
||
316 | } |
||
317 | } else { |
||
318 | if (smbc_unlink(h->filename) < 0) { |
||
319 | ret = AVERROR(errno); |
||
320 | goto cleanup; |
||
321 | } |
||
322 | } |
||
323 | |||
324 | ret = 0; |
||
325 | |||
326 | cleanup: |
||
327 | libsmbc_close(h); |
||
328 | return ret; |
||
329 | } |
||
330 | |||
331 | static int libsmbc_move(URLContext *h_src, URLContext *h_dst) |
||
332 | { |
||
333 | LIBSMBContext *libsmbc = h_src->priv_data; |
||
334 | int ret; |
||
335 | |||
336 | if ((ret = libsmbc_connect(h_src)) < 0) |
||
337 | goto cleanup; |
||
338 | |||
339 | if ((libsmbc->dh = smbc_rename(h_src->filename, h_dst->filename)) < 0) { |
||
340 | ret = AVERROR(errno); |
||
341 | goto cleanup; |
||
342 | } |
||
343 | |||
344 | ret = 0; |
||
345 | |||
346 | cleanup: |
||
347 | libsmbc_close(h_src); |
||
348 | return ret; |
||
349 | } |
||
350 | |||
351 | #define OFFSET(x) offsetof(LIBSMBContext, x) |
||
352 | #define D AV_OPT_FLAG_DECODING_PARAM |
||
353 | #define E AV_OPT_FLAG_ENCODING_PARAM |
||
354 | static const AVOption options[] = { |
||
355 | {"timeout", "set timeout in ms of socket I/O operations", OFFSET(timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E }, |
||
356 | {"truncate", "truncate existing files on write", OFFSET(trunc), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, E }, |
||
357 | {"workgroup", "set the workgroup used for making connections", OFFSET(workgroup), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E }, |
||
358 | {NULL} |
||
359 | }; |
||
360 | |||
361 | static const AVClass libsmbclient_context_class = { |
||
362 | .class_name = "libsmbc", |
||
363 | .item_name = av_default_item_name, |
||
364 | .option = options, |
||
365 | .version = LIBAVUTIL_VERSION_INT, |
||
366 | }; |
||
367 | |||
368 | URLProtocol ff_libsmbclient_protocol = { |
||
369 | .name = "smb", |
||
370 | .url_open = libsmbc_open, |
||
371 | .url_read = libsmbc_read, |
||
372 | .url_write = libsmbc_write, |
||
373 | .url_seek = libsmbc_seek, |
||
374 | .url_close = libsmbc_close, |
||
375 | .url_delete = libsmbc_delete, |
||
376 | .url_move = libsmbc_move, |
||
377 | .url_open_dir = libsmbc_open_dir, |
||
378 | .url_read_dir = libsmbc_read_dir, |
||
379 | .url_close_dir = libsmbc_close_dir, |
||
380 | .priv_data_size = sizeof(LIBSMBContext), |
||
381 | .priv_data_class = &libsmbclient_context_class, |
||
382 | .flags = URL_PROTOCOL_FLAG_NETWORK, |
||
383 | };>>>>>>>>>>>>>>>> |