FFmpeg
libsmbclient.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014 Lukasz Marek <lukasz.m.luki@gmail.com>
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 <libsmbclient.h>
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;
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 
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 
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 ? bytes_read : AVERROR_EOF;
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 
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 
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 
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 
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 
362  .class_name = "libsmbc",
363  .item_name = av_default_item_name,
364  .option = options,
365  .version = LIBAVUTIL_VERSION_INT,
366 };
367 
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,
383 };
AV_LOG_WARNING
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:182
AVERROR
Filter the word “frame” indicates either a video frame or a group of audio as stored in an AVFrame structure Format for each input and each output the list of supported formats For video that means pixel format For audio that means channel sample they are references to shared objects When the negotiation mechanism computes the intersection of the formats supported at each end of a all references to both lists are replaced with a reference to the intersection And when a single format is eventually chosen for a link amongst the remaining all references to the list are updated That means that if a filter requires that its input and output have the same format amongst a supported all it has to do is use a reference to the same list of formats query_formats can leave some formats unset and return AVERROR(EAGAIN) to cause the negotiation mechanism toagain later. That can be used by filters with complex requirements to use the format negotiated on one link to set the formats supported on another. Frame references ownership and permissions
opt.h
libsmbc_seek
static int64_t libsmbc_seek(URLContext *h, int64_t pos, int whence)
Definition: libsmbclient.c:136
URLContext::filename
char * filename
specified URL
Definition: url.h:42
URL_PROTOCOL_FLAG_NETWORK
#define URL_PROTOCOL_FLAG_NETWORK
Definition: url.h:34
AVERROR_EOF
#define AVERROR_EOF
End of file.
Definition: error.h:55
options
static const AVOption options[]
Definition: libsmbclient.c:354
cleanup
static av_cold void cleanup(FlashSV2Context *s)
Definition: flashsv2enc.c:127
AVIODirEntry::type
int type
Type of the entry.
Definition: avio.h:88
AVOption
AVOption.
Definition: opt.h:246
AVSEEK_SIZE
#define AVSEEK_SIZE
ORing this as the "whence" parameter to a seek function causes it to return the filesize without seek...
Definition: avio.h:531
LIBSMBContext::trunc
int trunc
Definition: libsmbclient.c:34
LIBSMBContext::dh
int dh
Definition: libsmbclient.c:31
URLProtocol
Definition: url.h:54
AVIO_ENTRY_UNKNOWN
@ AVIO_ENTRY_UNKNOWN
Definition: avio.h:67
AVIODirEntry::access_timestamp
int64_t access_timestamp
Time of last access in microseconds since unix epoch, -1 if unknown.
Definition: avio.h:94
av_append_path_component
char * av_append_path_component(const char *path, const char *component)
Append path component to the existing path.
Definition: avstring.c:302
AVIO_ENTRY_DIRECTORY
@ AVIO_ENTRY_DIRECTORY
Definition: avio.h:70
LIBSMBContext::ctx
SMBCCTX * ctx
Definition: libsmbclient.c:30
libsmbc_delete
static int libsmbc_delete(URLContext *h)
Definition: libsmbclient.c:290
fail
#define fail()
Definition: checkasm.h:123
LIBSMBContext::workgroup
char * workgroup
Definition: libsmbclient.c:36
LIBSMBContext::timeout
int timeout
Definition: libsmbclient.c:35
URLContext::priv_data
void * priv_data
Definition: url.h:41
AVIODirEntry::modification_timestamp
int64_t modification_timestamp
Time of last modification in microseconds since unix epoch, -1 if unknown.
Definition: avio.h:92
AV_LOG_ERROR
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
av_cold
#define av_cold
Definition: attributes.h:90
libsmbc_close
static av_cold int libsmbc_close(URLContext *h)
Definition: libsmbclient.c:81
LIBSMBContext::fd
int fd
Definition: libsmbclient.c:32
AVIO_FLAG_WRITE
#define AVIO_FLAG_WRITE
write-only
Definition: avio.h:675
D
#define D
Definition: libsmbclient.c:352
libsmbc_open
static av_cold int libsmbc_open(URLContext *h, const char *url, int flags)
Definition: libsmbclient.c:95
internal.h
LIBAVUTIL_VERSION_INT
#define LIBAVUTIL_VERSION_INT
Definition: version.h:85
AVClass
Describe the class of an AVClass context structure.
Definition: log.h:67
NULL
#define NULL
Definition: coverity.c:32
ff_libsmbclient_protocol
const URLProtocol ff_libsmbclient_protocol
Definition: libsmbclient.c:368
AVIODirEntry::size
int64_t size
File size in bytes, -1 if unknown.
Definition: avio.h:91
AVIODirEntry::name
char * name
Filename.
Definition: avio.h:87
av_default_item_name
const char * av_default_item_name(void *ptr)
Return the context name.
Definition: log.c:235
AVIO_ENTRY_FILE
@ AVIO_ENTRY_FILE
Definition: avio.h:74
AVIODirEntry::group_id
int64_t group_id
Group ID of owner, -1 if unknown.
Definition: avio.h:99
libsmbc_get_auth_data
static void libsmbc_get_auth_data(SMBCCTX *c, const char *server, const char *share, char *workgroup, int workgroup_len, char *username, int username_len, char *password, int password_len)
Definition: libsmbclient.c:39
c
Undefined Behavior In the C some operations are like signed integer dereferencing freed accessing outside allocated Undefined Behavior must not occur in a C it is not safe even if the output of undefined operations is unused The unsafety may seem nit picking but Optimizing compilers have in fact optimized code on the assumption that no undefined Behavior occurs Optimizing code based on wrong assumptions can and has in some cases lead to effects beyond the output of computations The signed integer overflow problem in speed critical code Code which is highly optimized and works with signed integers sometimes has the problem that often the output of the computation does not c
Definition: undefined.txt:32
trunc
static av_always_inline av_const double trunc(double x)
Definition: libm.h:458
AVIODirEntry::filemode
int64_t filemode
Unix file mode, -1 if unknown.
Definition: avio.h:100
size
int size
Definition: twinvq_data.h:11134
AVIODirEntry
Describes single entry of the directory.
Definition: avio.h:86
libsmbc_open_dir
static int libsmbc_open_dir(URLContext *h)
Definition: libsmbclient.c:186
URLProtocol::name
const char * name
Definition: url.h:55
AVIODirEntry::status_change_timestamp
int64_t status_change_timestamp
Time of last status change in microseconds since unix epoch, -1 if unknown.
Definition: avio.h:96
libsmbc_close_dir
static int libsmbc_close_dir(URLContext *h)
Definition: libsmbclient.c:279
libsmbclient_context_class
static const AVClass libsmbclient_context_class
Definition: libsmbclient.c:361
OFFSET
#define OFFSET(x)
Definition: libsmbclient.c:351
AVIODirEntry::user_id
int64_t user_id
User ID of owner, -1 if unknown.
Definition: avio.h:98
ff_alloc_dir_entry
AVIODirEntry * ff_alloc_dir_entry(void)
Allocate directory entry with default values.
Definition: url.c:325
URLContext
Definition: url.h:38
libsmbc_move
static int libsmbc_move(URLContext *h_src, URLContext *h_dst)
Definition: libsmbclient.c:331
libsmbc_read_dir
static int libsmbc_read_dir(URLContext *h, AVIODirEntry **next)
Definition: libsmbclient.c:207
url.h
ret
ret
Definition: filter_design.txt:187
AVClass::class_name
const char * class_name
The name of the class; usually it is the same name as the context structure type to which the AVClass...
Definition: log.h:72
pos
unsigned int pos
Definition: spdifenc.c:412
avformat.h
AV_OPT_TYPE_INT
@ AV_OPT_TYPE_INT
Definition: opt.h:223
LIBSMBContext::filesize
int64_t filesize
Definition: libsmbclient.c:33
E
#define E
Definition: libsmbclient.c:353
AVIO_ENTRY_WORKGROUP
@ AVIO_ENTRY_WORKGROUP
Definition: avio.h:77
AVIO_FLAG_READ
#define AVIO_FLAG_READ
read-only
Definition: avio.h:674
av_strdup
char * av_strdup(const char *s)
Duplicate a string.
Definition: mem.c:253
av_free
#define av_free(p)
Definition: tableprint_vlc.h:34
libsmbc_connect
static av_cold int libsmbc_connect(URLContext *h)
Definition: libsmbclient.c:48
av_freep
#define av_freep(p)
Definition: tableprint_vlc.h:35
AVIO_ENTRY_SERVER
@ AVIO_ENTRY_SERVER
Definition: avio.h:75
libsmbc_read
static int libsmbc_read(URLContext *h, unsigned char *buf, int size)
Definition: libsmbclient.c:158
LIBSMBContext
Definition: libsmbclient.c:28
flags
#define flags(name, subs,...)
Definition: cbs_av1.c:565
av_log
#define av_log(a,...)
Definition: tableprint_vlc.h:28
h
h
Definition: vp9dsp_template.c:2038
avstring.h
AV_OPT_TYPE_STRING
@ AV_OPT_TYPE_STRING
Definition: opt.h:227
libsmbc_write
static int libsmbc_write(URLContext *h, const unsigned char *buf, int size)
Definition: libsmbclient.c:172
AVIO_ENTRY_SHARE
@ AVIO_ENTRY_SHARE
Definition: avio.h:76