Subversion Repositories Kolibri OS

Rev

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
};