mirror of
https://gitee.com/Vancouver2017/luban-lite-t3e-pro.git
synced 2025-12-14 10:28:54 +00:00
704 lines
15 KiB
C
704 lines
15 KiB
C
/*
|
|
* Copyright (c) 2018, Real-Thread Information Technology Ltd
|
|
* All rights reserved
|
|
*
|
|
* Copyright (c) 2006-2018, RT-Thread Development Team
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
* 2018-11-11 heyuanjie87 the first version
|
|
*/
|
|
|
|
#include <adb_service.h>
|
|
#include "file_sync_service.h"
|
|
#include "file_exmod.h"
|
|
#include <dfs_posix.h>
|
|
|
|
#ifdef LPKG_ADB_FILESYNC_MD5_ENABLE
|
|
#include <tiny_md5.h>
|
|
#endif
|
|
|
|
#ifndef LPKG_ADB_FILESYNC_STACK_SIZE
|
|
#define LPKG_ADB_FILESYNC_STACK_SIZE 2304
|
|
#endif
|
|
|
|
#ifndef LPKG_ADB_FILESYNC_RECV_TIMEOUT
|
|
#define LPKG_ADB_FILESYNC_RECV_TIMEOUT 2000
|
|
#endif
|
|
|
|
#define DBG_ENABLE
|
|
#define DBG_SECTION_NAME "ADB sync"
|
|
#define DBG_LEVEL DBG_INFO
|
|
#define DBG_COLOR
|
|
#include <rtdbg.h>
|
|
|
|
static char* sync_string_new(char *s1, char *s2, int s2n)
|
|
{
|
|
char *s;
|
|
int len;
|
|
|
|
len = rt_strlen(s1);
|
|
s = rt_malloc(len + s2n + 1);
|
|
if (s)
|
|
{
|
|
rt_memcpy(s, s1, len);
|
|
rt_memcpy(&s[len], s2, s2n);
|
|
s[len + s2n] = 0;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
static bool sync_read(struct adb_service *ser, void *buf, int size, int ms)
|
|
{
|
|
struct filesync_ext *ext = (struct filesync_ext *)ser->extptr;
|
|
int len;
|
|
struct adb_packet *p;
|
|
char *dpos = (char*)buf;
|
|
char *spos;
|
|
|
|
while (size)
|
|
{
|
|
if (!ext->cur)
|
|
{
|
|
if (!adb_packet_dequeue(&ext->recv_que, &ext->cur, ms))
|
|
{
|
|
return false;
|
|
}
|
|
ext->cur->split = 0; //used as postion of read
|
|
}
|
|
|
|
p = ext->cur;
|
|
spos = (char*)(p->payload + p->split);
|
|
|
|
len = size > p->msg.data_length ? p->msg.data_length : size;
|
|
rt_memcpy(dpos, spos, len);
|
|
p->msg.data_length -= len;
|
|
p->split += len;
|
|
dpos += len;
|
|
size -= len;
|
|
|
|
if (p->msg.data_length == 0)
|
|
{
|
|
ext->cur = 0;
|
|
adb_packet_delete(p);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool sync_write(struct adb_service *ser, void *buf, int size, int ms)
|
|
{
|
|
struct adb_packet *p;
|
|
|
|
p = adb_packet_new(size);
|
|
if (!p)
|
|
{
|
|
return false;
|
|
}
|
|
rt_memcpy(p->payload, buf, size);
|
|
if (!adb_service_sendpacket(ser, p, ms))
|
|
{
|
|
adb_packet_delete(p);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool SendSyncFail(struct adb_service *ser, const char *reason)
|
|
{
|
|
union file_syncmsg msg;
|
|
bool ret;
|
|
|
|
LOG_E("fail - %s", reason);
|
|
|
|
msg.data.id = ID_FAIL;
|
|
msg.data.size = rt_strlen(reason);
|
|
|
|
ret = sync_write(ser, &msg.data, sizeof(msg.data), 50);
|
|
if (ret)
|
|
ret = sync_write(ser, (void*)reason, msg.data.size, 50);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool sync_read_path(struct adb_service *ser, char **name, int len)
|
|
{
|
|
*name = 0;
|
|
if (len == 0)
|
|
return true;
|
|
|
|
if (len > 1024)
|
|
{
|
|
SendSyncFail(ser, "path too long");
|
|
return false;
|
|
}
|
|
*name = rt_malloc(len + 1);
|
|
if (!*name)
|
|
{
|
|
SendSyncFail(ser, "alloc memory failure");
|
|
return false;
|
|
}
|
|
if (!sync_read(ser, *name, len, LPKG_ADB_FILESYNC_RECV_TIMEOUT))
|
|
{
|
|
SendSyncFail(ser, "filename read failure");
|
|
rt_free(*name);
|
|
*name = 0;
|
|
return false;
|
|
}
|
|
(*name)[len] = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool do_lstat_v1(struct adb_service *ser, const char* path)
|
|
{
|
|
union file_syncmsg msg = {0};
|
|
struct stat st = {0};
|
|
|
|
msg.stat_v1.id = ID_LSTAT_V1;
|
|
|
|
stat(path, &st);
|
|
msg.stat_v1.mode = st.st_mode;
|
|
msg.stat_v1.size = st.st_size;
|
|
msg.stat_v1.time = st.st_mtime;
|
|
|
|
return sync_write(ser, &msg.stat_v1, sizeof(msg.stat_v1), 50);
|
|
}
|
|
|
|
static bool do_recv(struct adb_service *ser, char* path, char* buffer)
|
|
{
|
|
union file_syncmsg msg;
|
|
int fd;
|
|
bool ret = false;
|
|
|
|
fd = open(path, O_RDONLY);
|
|
if (fd < 0)
|
|
{
|
|
SendSyncFail(ser, "open failed");
|
|
return false;
|
|
}
|
|
|
|
msg.data.id = ID_DATA;
|
|
while (1)
|
|
{
|
|
int r = read(fd, &buffer[0], SYNC_DATA_MAX);
|
|
if (r <= 0)
|
|
{
|
|
if (r == 0)
|
|
break;
|
|
SendSyncFail(ser, "read failed");
|
|
close(fd);
|
|
return false;
|
|
}
|
|
|
|
msg.data.size = r;
|
|
ret = sync_write(ser, &msg.data, sizeof(msg.data), 400);
|
|
if (ret)
|
|
ret = sync_write(ser, &buffer[0], r, 100);
|
|
if (!ret)
|
|
{
|
|
close(fd);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
close(fd);
|
|
|
|
msg.data.id = ID_DONE;
|
|
msg.data.size = 0;
|
|
return sync_write(ser, &msg.data, sizeof(msg.data), 200);
|
|
}
|
|
|
|
static bool do_list(struct adb_service *ser, char* path)
|
|
{
|
|
union file_syncmsg msg;
|
|
struct dirent *de;
|
|
DIR* d;
|
|
bool ret = true;
|
|
|
|
msg.dent.id = ID_DENT;
|
|
|
|
d = opendir(path);
|
|
if (!d)
|
|
goto done;
|
|
|
|
while (1)
|
|
{
|
|
struct stat st = {0};
|
|
char *filename;
|
|
int d_name_length;
|
|
|
|
de = readdir(d);
|
|
if (!de)
|
|
break;
|
|
|
|
d_name_length = rt_strlen(de->d_name);
|
|
filename = sync_string_new(path, de->d_name, d_name_length);
|
|
if (!filename)
|
|
{
|
|
ret = false;
|
|
SendSyncFail(ser, "alloc memory fail");
|
|
goto fail;
|
|
}
|
|
if (stat(filename, &st) == 0)
|
|
{
|
|
msg.dent.mode = st.st_mode;
|
|
msg.dent.size = st.st_size;
|
|
msg.dent.time = st.st_mtime;
|
|
msg.dent.namelen = d_name_length;
|
|
|
|
ret = sync_write(ser, &msg.dent, sizeof(msg.dent), 200);
|
|
if (ret)
|
|
ret = sync_write(ser, de->d_name, d_name_length, 100);
|
|
if (!ret)
|
|
{
|
|
LOG_E("sync: list write fail");
|
|
rt_free(filename);
|
|
goto fail;
|
|
}
|
|
}
|
|
rt_free(filename);
|
|
}
|
|
|
|
fail:
|
|
closedir(d);
|
|
|
|
done:
|
|
if (ret)
|
|
{
|
|
msg.dent.id = ID_DONE;
|
|
msg.dent.mode = 0;
|
|
msg.dent.size = 0;
|
|
msg.dent.time = 0;
|
|
msg.dent.namelen = 0;
|
|
|
|
ret = sync_write(ser, &msg.dent, sizeof(msg.dent), 50);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool hasdir(char *path)
|
|
{
|
|
struct stat st = {0};
|
|
|
|
if (stat(path, &st) < 0)
|
|
return false;
|
|
|
|
if (st.st_mode & S_IFDIR)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool secure_mkdirs(char *path)
|
|
{
|
|
int plen;
|
|
int pos;
|
|
int sub;
|
|
|
|
if (path[0] != '/')
|
|
return false;
|
|
|
|
plen = rt_strlen(path);
|
|
for (pos = 0; pos < plen; pos ++)
|
|
{
|
|
if (path[pos] != '/')
|
|
continue;
|
|
|
|
for (sub = pos + 1; sub < plen; sub ++)
|
|
{
|
|
if (path[sub] != '/')
|
|
continue;
|
|
path[sub] = 0;
|
|
if (hasdir(path))
|
|
{
|
|
path[sub] = '/';
|
|
break;
|
|
}
|
|
|
|
if (mkdir(path, 0) < 0)
|
|
return false;
|
|
path[sub] = '/';
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool handle_send_file(struct adb_service *ser, char* path,
|
|
mode_t mode, char* buffer, bool do_unlink)
|
|
{
|
|
union file_syncmsg msg;
|
|
int fd;
|
|
|
|
fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
|
|
|
|
if (fd < 0)
|
|
{
|
|
int eno;
|
|
|
|
eno = rt_get_errno();
|
|
if (eno == ENOENT || eno == -ENOENT)
|
|
{
|
|
if (!secure_mkdirs(path))
|
|
{
|
|
SendSyncFail(ser, "secure_mkdirs failed");
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
fd = open(path, O_WRONLY | O_CREAT | O_EXCL, mode);
|
|
}
|
|
|
|
if (fd < 0)
|
|
{
|
|
SendSyncFail(ser, "couldn't create file");
|
|
goto fail;
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
if (!sync_read(ser, &msg.data, sizeof(msg.data), LPKG_ADB_FILESYNC_RECV_TIMEOUT))
|
|
{
|
|
SendSyncFail(ser, "read data head fail");
|
|
goto fail;
|
|
}
|
|
|
|
if (msg.data.id != ID_DATA)
|
|
{
|
|
if (msg.data.id == ID_DONE)
|
|
{
|
|
break;
|
|
}
|
|
SendSyncFail(ser, "invalid data message");
|
|
goto abort;
|
|
}
|
|
|
|
/* recv file data */
|
|
while (msg.data.size > 0)
|
|
{
|
|
int size = (msg.data.size > SYNC_DATA_MAX) ?
|
|
SYNC_DATA_MAX : msg.data.size;
|
|
|
|
if (!sync_read(ser, &buffer[0], size, LPKG_ADB_FILESYNC_RECV_TIMEOUT))
|
|
{
|
|
SendSyncFail(ser, "read packet timeout");
|
|
goto abort;
|
|
}
|
|
#ifndef FILESYNC_DATA_NO_WRITE /* for test */
|
|
if (write(fd, &buffer[0], size) != size)
|
|
{
|
|
SendSyncFail(ser, "write failed");
|
|
goto fail;
|
|
}
|
|
#endif
|
|
msg.data.size -= size;
|
|
}
|
|
}
|
|
|
|
close(fd);
|
|
|
|
msg.status.id = ID_OKAY;
|
|
msg.status.msglen = 0;
|
|
return sync_write(ser, &msg.status, sizeof(msg.status), 100);
|
|
|
|
fail:
|
|
abort:
|
|
if (fd >= 0)
|
|
close(fd);
|
|
if (do_unlink)
|
|
unlink(path);
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool do_send(struct adb_service *ser, char* spec, char *buffer)
|
|
{
|
|
char *path;
|
|
mode_t mode = 0;
|
|
bool do_unlink = true;
|
|
int comma = 0;
|
|
|
|
while (spec[comma] && (spec[comma] != ','))
|
|
comma ++;
|
|
|
|
if (comma == rt_strlen(spec))
|
|
{
|
|
SendSyncFail(ser, "missing , in ID_SEND");
|
|
return false;
|
|
}
|
|
|
|
spec[comma] = 0;
|
|
path = spec;
|
|
|
|
return handle_send_file(ser, path, mode, buffer, do_unlink);
|
|
}
|
|
|
|
#ifdef LPKG_ADB_FILESYNC_MD5_ENABLE
|
|
static bool do_calcmd5(struct adb_service *ser, char* spec, char *buffer)
|
|
{
|
|
union file_syncmsg msg;
|
|
tiny_md5_context c;
|
|
int fd;
|
|
|
|
fd = open(spec, O_RDONLY, 0);
|
|
if (fd < 0)
|
|
{
|
|
SendSyncFail(ser, "md5 open file fail");
|
|
return false;
|
|
}
|
|
|
|
tiny_md5_starts(&c);
|
|
while (1)
|
|
{
|
|
int len = read(fd, buffer, SYNC_DATA_MAX);
|
|
if (len < 0)
|
|
{
|
|
close(fd);
|
|
SendSyncFail(ser, "md5 read io fail");
|
|
return false;
|
|
}
|
|
else if (len == 0)
|
|
break;
|
|
|
|
tiny_md5_update(&c, (unsigned char*)buffer, len);
|
|
}
|
|
tiny_md5_finish(&c, msg.md5.value);
|
|
close(fd);
|
|
|
|
msg.md5.id = ID_CMD5;
|
|
return sync_write(ser, &msg.md5, sizeof(msg.md5), 50);
|
|
}
|
|
#else
|
|
static bool do_calcmd5(struct adb_service *ser, char* spec, char *buffer)
|
|
{
|
|
((void)spec);
|
|
((void)buffer);
|
|
SendSyncFail(ser, "ID_CMD5 not enabled");
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
static bool handle_sync_command(struct adb_service *ser, char *buffer)
|
|
{
|
|
bool ret = false;
|
|
struct file_syncreq req;
|
|
char *name;
|
|
#ifdef LPKG_ADB_EXTERNAL_MOD_ENABLE
|
|
struct f_exmod *exmod;
|
|
#endif
|
|
|
|
if (!buffer)
|
|
{
|
|
SendSyncFail(ser, "buffer is null");
|
|
return false;
|
|
}
|
|
|
|
if (!sync_read(ser, &req, sizeof(req), LPKG_ADB_FILESYNC_RECV_TIMEOUT))
|
|
{
|
|
SendSyncFail(ser, "command read failure");
|
|
return false;
|
|
}
|
|
|
|
if (!sync_read_path(ser, &name, req.path_length))
|
|
return false;
|
|
#ifdef LPKG_ADB_EXTERNAL_MOD_ENABLE
|
|
exmod = file_exmod_create(name);
|
|
if (exmod != NULL)
|
|
{
|
|
exmod->ser = ser;
|
|
}
|
|
#endif
|
|
switch (req.id)
|
|
{
|
|
case ID_LSTAT_V1:
|
|
#ifdef LPKG_ADB_EXTERNAL_MOD_ENABLE
|
|
if (exmod != RT_NULL)
|
|
{
|
|
ret = file_exmod_do_lstat_v1(ser, exmod);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
ret = do_lstat_v1(ser, name);
|
|
}
|
|
break;
|
|
case ID_LSTAT_V2:
|
|
case ID_STAT_V2:
|
|
break;
|
|
case ID_LIST:
|
|
#ifdef LPKG_ADB_EXTERNAL_MOD_ENABLE
|
|
if (exmod != RT_NULL)
|
|
{
|
|
ret = file_exmod_do_list(ser, exmod);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
ret = do_list(ser, name);
|
|
}
|
|
break;
|
|
case ID_SEND:
|
|
#ifdef LPKG_ADB_EXTERNAL_MOD_ENABLE
|
|
if (exmod != RT_NULL)
|
|
{
|
|
ret = file_exmod_do_send(ser, exmod);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
ret = do_send(ser, name, buffer);
|
|
}
|
|
break;
|
|
case ID_RECV:
|
|
#ifdef LPKG_ADB_EXTERNAL_MOD_ENABLE
|
|
if (exmod != RT_NULL)
|
|
{
|
|
ret = file_exmod_do_recv(ser, exmod);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
ret = do_recv(ser, name, buffer);
|
|
}
|
|
break;
|
|
case ID_CMD5:
|
|
ret = do_calcmd5(ser, name, buffer);
|
|
break;
|
|
case ID_QUIT:
|
|
break;
|
|
default:
|
|
SendSyncFail(ser, "unknown command");
|
|
break;
|
|
}
|
|
rt_free(name);
|
|
#ifdef LPKG_ADB_EXTERNAL_MOD_ENABLE
|
|
file_exmod_delete(exmod);
|
|
#endif
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void filesync_thread(void *arg)
|
|
{
|
|
struct adb_service *ser = (struct adb_service *)arg;
|
|
struct filesync_ext *ext = (struct filesync_ext *)ser->extptr;
|
|
char *buf;
|
|
|
|
LOG_D("start");
|
|
buf = rt_malloc(SYNC_DATA_MAX);
|
|
|
|
while (handle_sync_command(ser, buf)){}
|
|
|
|
ser->online = 0;
|
|
rt_free(buf);
|
|
adb_packet_delete(ext->cur);
|
|
adb_packet_clear(&ext->recv_que);
|
|
adb_service_close_report(ser);
|
|
|
|
rt_event_send(&ext->join, 1);
|
|
|
|
LOG_D("quit");
|
|
}
|
|
|
|
static int _filesync_open(struct adb_service *ser, char *args)
|
|
{
|
|
int ret = -1;
|
|
struct filesync_ext *ext;
|
|
|
|
ext = (struct filesync_ext *)ser->extptr;
|
|
rt_mb_init(&ext->recv_que, "sync", ext->rque_buf,
|
|
sizeof(ext->rque_buf)/sizeof(ext->rque_buf[0]), 0);
|
|
rt_event_init(&ext->join, "sync", 0);
|
|
|
|
ret = rt_thread_startup(ext->worker);
|
|
ser->online = 1;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int _filesync_close(struct adb_service *ser)
|
|
{
|
|
int ret = 0;
|
|
struct filesync_ext *ext;
|
|
|
|
ext = (struct filesync_ext *)ser->extptr;
|
|
ser->online = 0;
|
|
rt_event_recv(&ext->join, 1, RT_EVENT_FLAG_OR,
|
|
rt_tick_from_millisecond(LPKG_ADB_FILESYNC_RECV_TIMEOUT * 2), 0);
|
|
rt_event_detach(&ext->join);
|
|
rt_mb_detach(&ext->recv_que);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static bool _filesync_enqueue(struct adb_service * ser, struct adb_packet *p, int ms)
|
|
{
|
|
struct filesync_ext *ext;
|
|
bool ret;
|
|
|
|
if (!ser->online)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
ext = (struct filesync_ext *)ser->extptr;
|
|
ret = adb_packet_enqueue(&ext->recv_que, p, LPKG_ADB_FILESYNC_RECV_TIMEOUT);
|
|
return ret;
|
|
}
|
|
|
|
static const struct adb_service_ops _ops =
|
|
{
|
|
_filesync_open,
|
|
_filesync_close,
|
|
_filesync_enqueue
|
|
};
|
|
|
|
static struct adb_service *_filesync_create(struct adb_service_handler *h)
|
|
{
|
|
struct adb_service *ser;
|
|
struct filesync_ext *ext;
|
|
|
|
ser = adb_service_alloc(&_ops, sizeof(struct filesync_ext));
|
|
if (!ser)
|
|
return ser;
|
|
ext = (struct filesync_ext *)ser->extptr;
|
|
|
|
ext->worker = rt_thread_create("sync:",
|
|
filesync_thread,
|
|
ser,
|
|
LPKG_ADB_FILESYNC_STACK_SIZE,
|
|
22,
|
|
20);
|
|
if (!ext->worker)
|
|
{
|
|
rt_free(ser);
|
|
return RT_NULL;
|
|
}
|
|
|
|
return ser;
|
|
}
|
|
|
|
static void _filesync_destroy(struct adb_service_handler *h, struct adb_service *s)
|
|
{
|
|
rt_free(s);
|
|
}
|
|
|
|
int adb_filesync_init(void)
|
|
{
|
|
static struct adb_service_handler _h;
|
|
|
|
_h.name = "sync:";
|
|
_h.create = _filesync_create;
|
|
_h.destroy = _filesync_destroy;
|
|
|
|
return adb_service_handler_register(&_h);
|
|
}
|
|
INIT_APP_EXPORT(adb_filesync_init);
|