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 28typedef struct { 29 const AVClass *class; 30 SMBCCTX *ctx; 31 int fd; 32 int64_t filesize; 33 int trunc; 34 int timeout; 35 char *workgroup; 36} LIBSMBContext; 37 38static void libsmbc_get_auth_data(SMBCCTX *c, const char *server, const char *share, 39 char *workgroup, int workgroup_len, 40 char *username, int username_len, 41 char *password, int password_len) 42{ 43 /* Do nothing yet. Credentials are passed via url. 44 * Callback must exists, there might be a segmentation fault otherwise. */ 45} 46 47static av_cold int libsmbc_connect(URLContext *h) 48{ 49 LIBSMBContext *libsmbc = h->priv_data; 50 51 libsmbc->ctx = smbc_new_context(); 52 if (!libsmbc->ctx) { 53 av_log(h, AV_LOG_ERROR, "Cannot create context: %s.\n", strerror(errno)); 54 return AVERROR(errno); 55 } 56 if (!smbc_init_context(libsmbc->ctx)) { 57 av_log(h, AV_LOG_ERROR, "Cannot initialize context: %s.\n", strerror(errno)); 58 return AVERROR(errno); 59 } 60 smbc_set_context(libsmbc->ctx); 61 62 smbc_setOptionUserData(libsmbc->ctx, h); 63 smbc_setFunctionAuthDataWithContext(libsmbc->ctx, libsmbc_get_auth_data); 64 65 if (libsmbc->timeout != -1) 66 smbc_setTimeout(libsmbc->ctx, libsmbc->timeout); 67 if (libsmbc->workgroup) 68 smbc_setWorkgroup(libsmbc->ctx, libsmbc->workgroup); 69 70 if (smbc_init(NULL, 0) < 0) { 71 av_log(h, AV_LOG_ERROR, "Initialization failed: %s\n", strerror(errno)); 72 return AVERROR(errno); 73 } 74 return 0; 75} 76 77static av_cold int libsmbc_close(URLContext *h) 78{ 79 LIBSMBContext *libsmbc = h->priv_data; 80 if (libsmbc->fd >= 0) { 81 smbc_close(libsmbc->fd); 82 libsmbc->fd = -1; 83 } 84 if (libsmbc->ctx) { 85 smbc_free_context(libsmbc->ctx, 1); 86 libsmbc->ctx = NULL; 87 } 88 return 0; 89} 90 91static av_cold int libsmbc_open(URLContext *h, const char *url, int flags) 92{ 93 LIBSMBContext *libsmbc = h->priv_data; 94 int access, ret; 95 struct stat st; 96 97 libsmbc->fd = -1; 98 libsmbc->filesize = -1; 99 100 if ((ret = libsmbc_connect(h)) < 0) 101 goto fail; 102 103 if ((flags & AVIO_FLAG_WRITE) && (flags & AVIO_FLAG_READ)) { 104 access = O_CREAT | O_RDWR; 105 if (libsmbc->trunc) 106 access |= O_TRUNC; 107 } else if (flags & AVIO_FLAG_WRITE) { 108 access = O_CREAT | O_WRONLY; 109 if (libsmbc->trunc) 110 access |= O_TRUNC; 111 } else 112 access = O_RDONLY; 113 114 /* 0666 = -rw-rw-rw- = read+write for everyone, minus umask */ 115 if ((libsmbc->fd = smbc_open(url, access, 0666)) < 0) { 116 ret = AVERROR(errno); 117 av_log(h, AV_LOG_ERROR, "File open failed: %s\n", strerror(errno)); 118 goto fail; 119 } 120 121 if (smbc_fstat(libsmbc->fd, &st) < 0) 122 av_log(h, AV_LOG_WARNING, "Cannot stat file: %s\n", strerror(errno)); 123 else 124 libsmbc->filesize = st.st_size; 125 126 return 0; 127 fail: 128 libsmbc_close(h); 129 return ret; 130} 131 132static int64_t libsmbc_seek(URLContext *h, int64_t pos, int whence) 133{ 134 LIBSMBContext *libsmbc = h->priv_data; 135 int64_t newpos; 136 137 if (whence == AVSEEK_SIZE) { 138 if (libsmbc->filesize == -1) { 139 av_log(h, AV_LOG_ERROR, "Error during seeking: filesize is unknown.\n"); 140 return AVERROR(EIO); 141 } else 142 return libsmbc->filesize; 143 } 144 145 if ((newpos = smbc_lseek(libsmbc->fd, pos, whence)) < 0) { 146 int err = errno; 147 av_log(h, AV_LOG_ERROR, "Error during seeking: %s\n", strerror(err)); 148 return AVERROR(err); 149 } 150 151 return newpos; 152} 153 154static int libsmbc_read(URLContext *h, unsigned char *buf, int size) 155{ 156 LIBSMBContext *libsmbc = h->priv_data; 157 int bytes_read; 158 159 if ((bytes_read = smbc_read(libsmbc->fd, buf, size)) < 0) { 160 av_log(h, AV_LOG_ERROR, "Read error: %s\n", strerror(errno)); 161 return AVERROR(errno); 162 } 163 164 return bytes_read; 165} 166 167static int libsmbc_write(URLContext *h, const unsigned char *buf, int size) 168{ 169 LIBSMBContext *libsmbc = h->priv_data; 170 int bytes_written; 171 172 if ((bytes_written = smbc_write(libsmbc->fd, buf, size)) < 0) { 173 av_log(h, AV_LOG_ERROR, "Write error: %s\n", strerror(errno)); 174 return AVERROR(errno); 175 } 176 177 return bytes_written; 178} 179 180#define OFFSET(x) offsetof(LIBSMBContext, x) 181#define D AV_OPT_FLAG_DECODING_PARAM 182#define E AV_OPT_FLAG_ENCODING_PARAM 183static const AVOption options[] = { 184 {"timeout", "set timeout in ms of socket I/O operations", OFFSET(timeout), AV_OPT_TYPE_INT, {.i64 = -1}, -1, INT_MAX, D|E }, 185 {"truncate", "truncate existing files on write", OFFSET(trunc), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, E }, 186 {"workgroup", "set the workgroup used for making connections", OFFSET(workgroup), AV_OPT_TYPE_STRING, { 0 }, 0, 0, D|E }, 187 {NULL} 188}; 189 190static const AVClass libsmbclient_context_class = { 191 .class_name = "libsmbc", 192 .item_name = av_default_item_name, 193 .option = options, 194 .version = LIBAVUTIL_VERSION_INT, 195}; 196 197URLProtocol ff_libsmbclient_protocol = { 198 .name = "smb", 199 .url_open = libsmbc_open, 200 .url_read = libsmbc_read, 201 .url_write = libsmbc_write, 202 .url_seek = libsmbc_seek, 203 .url_close = libsmbc_close, 204 .priv_data_size = sizeof(LIBSMBContext), 205 .priv_data_class = &libsmbclient_context_class, 206 .flags = URL_PROTOCOL_FLAG_NETWORK, 207}; 208