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 };
#define D
Definition: libsmbclient.c:352
#define NULL
Definition: coverity.c:32
#define URL_PROTOCOL_FLAG_NETWORK
Definition: url.h:34
static const AVOption options[]
Definition: libsmbclient.c:354
AVOption.
Definition: opt.h:246
int64_t filemode
Unix file mode, -1 if unknown.
Definition: avio.h:100
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
#define AV_LOG_WARNING
Something somehow does not look correct.
Definition: log.h:182
#define LIBAVUTIL_VERSION_INT
Definition: version.h:85
Describes single entry of the directory.
Definition: avio.h:86
const char * av_default_item_name(void *ptr)
Return the context name.
Definition: log.c:191
#define AVIO_FLAG_READ
read-only
Definition: avio.h:674
#define AVIO_FLAG_WRITE
write-only
Definition: avio.h:675
int64_t filesize
Definition: libsmbclient.c:33
static int libsmbc_move(URLContext *h_src, URLContext *h_dst)
Definition: libsmbclient.c:331
static av_cold int libsmbc_connect(URLContext *h)
Definition: libsmbclient.c:48
static av_cold int libsmbc_close(URLContext *h)
Definition: libsmbclient.c:81
int64_t modification_timestamp
Time of last modification in microseconds since unix epoch, -1 if unknown.
Definition: avio.h:92
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
#define av_cold
Definition: attributes.h:82
char * workgroup
Definition: libsmbclient.c:36
AVOptions.
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
static int64_t libsmbc_seek(URLContext *h, int64_t pos, int whence)
Definition: libsmbclient.c:136
static int libsmbc_close_dir(URLContext *h)
Definition: libsmbclient.c:279
#define AVERROR_EOF
End of file.
Definition: error.h:55
ptrdiff_t size
Definition: opengl_enc.c:100
static int libsmbc_write(URLContext *h, const unsigned char *buf, int size)
Definition: libsmbclient.c:172
#define av_log(a,...)
char * name
Filename.
Definition: avio.h:87
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition: log.h:176
const URLProtocol ff_libsmbclient_protocol
Definition: libsmbclient.c:368
static int libsmbc_read_dir(URLContext *h, AVIODirEntry **next)
Definition: libsmbclient.c:207
#define fail()
Definition: checkasm.h:122
int64_t access_timestamp
Time of last access in microseconds since unix epoch, -1 if unknown.
Definition: avio.h:94
static av_always_inline av_const double trunc(double x)
Definition: libm.h:458
#define OFFSET(x)
Definition: libsmbclient.c:351
static int libsmbc_read(URLContext *h, unsigned char *buf, int size)
Definition: libsmbclient.c:158
int64_t size
File size in bytes, -1 if unknown.
Definition: avio.h:91
char * av_strdup(const char *s)
Duplicate a string.
Definition: mem.c:251
int type
Type of the entry.
Definition: avio.h:88
static int libsmbc_open_dir(URLContext *h)
Definition: libsmbclient.c:186
void * buf
Definition: avisynth_c.h:766
Definition: url.h:38
static int libsmbc_delete(URLContext *h)
Definition: libsmbclient.c:290
Describe the class of an AVClass context structure.
Definition: log.h:67
int64_t group_id
Group ID of owner, -1 if unknown.
Definition: avio.h:99
void * priv_data
Definition: url.h:41
const char * name
Definition: url.h:55
#define flags(name, subs,...)
Definition: cbs_av1.c:561
SMBCCTX * ctx
Definition: libsmbclient.c:30
Main libavformat public API header.
char * av_append_path_component(const char *path, const char *component)
Append path component to the existing path.
Definition: avstring.c:300
int64_t status_change_timestamp
Time of last status change in microseconds since unix epoch, -1 if unknown.
Definition: avio.h:96
AVIODirEntry * ff_alloc_dir_entry(void)
Allocate directory entry with default values.
Definition: url.c:149
char * filename
specified URL
Definition: url.h:42
#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
#define av_free(p)
#define E
Definition: libsmbclient.c:353
int64_t user_id
User ID of owner, -1 if unknown.
Definition: avio.h:98
static const AVClass libsmbclient_context_class
Definition: libsmbclient.c:361
#define av_freep(p)
unbuffered private I/O API
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
static av_cold int libsmbc_open(URLContext *h, const char *url, int flags)
Definition: libsmbclient.c:95
static av_cold void cleanup(FlashSV2Context *s)
Definition: flashsv2enc.c:127