--- a/dav/system.c Fri Aug 02 21:40:05 2019 +0200 +++ b/dav/system.c Fri Aug 02 22:04:00 2019 +0200 @@ -26,6 +26,8 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include <libidav/utils.h> + #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -34,6 +36,8 @@ #include <sys/types.h> #include <errno.h> +#include <ucx/string.h> + #ifndef _WIN32 #include <unistd.h> #endif @@ -48,6 +52,13 @@ #ifndef _WIN32 /* ---------- POSIX implementation ---------- */ +void sys_init(void) { + +} +void sys_uninit(void) { + +} + SYS_DIR sys_opendir(const char *path) { DIR *dir = opendir(path); if(!dir) { @@ -96,6 +107,13 @@ return lstat(path, s); } +int sys_islink(const char *path) { + struct stat s; + if(!lstat(path, &s)) { + return S_ISLNK(s.st_mode); + } +} return 0; + int sys_rename(const char *oldpath, const char *newpath) { return rename(oldpath, newpath); } @@ -108,8 +126,31 @@ return mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); } -ssize_t sys_readlink(const char *path, char *buffer, size_t size) { - return readlink(path, buffer, size); +char* sys_readlink(const char *path, SYS_STAT *s) { + char *ret = NULL; + + off_t l_sz = s->st_size + 16; + size_t lnksize = l_sz > 256 ? l_sz : 256; + char *lnkbuf = malloc(lnksize); + + ssize_t len = 0; + for(int i=0;i<4;i++) { + // we try to read the link at most 4 times + // only repeat if the buffer is too small + len = readlink(path, lnkbuf, lnksize); + if(len < lnksize) { + ret = lnkbuf; // success + lnkbuf[len] = 0; // terminate buffer + break; + } + lnksize *= 2; // retry with bigger buffer + lnkbuf = realloc(lnkbuf, lnksize); + } + + if(!ret) { + free(lnkbuf); + } + return ret; } int sys_symlink(const char *target, const char *linkpath) { @@ -126,6 +167,26 @@ #else /* ---------- Windows implementation ---------- */ +#include <windows.h> +#include <winnls.h> +#include <shobjidl.h> +#include <objbase.h> +#include <objidl.h> + +#include <direct.h> +#include <wchar.h> + +void sys_init(void) { + HRESULT res = CoInitialize(NULL); + if(res != S_OK) { + fprintf(stderr, "Error: CoInitialize failed\n"); + } +} + +void sys_uninit(void) { + CoUninitialize(); +} + static wchar_t* path2winpath(const char *path, int dir, int *newlen) { size_t len = strlen(path); size_t lenadd = dir ? 2 : 0; @@ -158,7 +219,25 @@ wpath[wlen] = 0; return wpath; -} +} + +static char* winpath2multibyte(const wchar_t *wpath, size_t wlen) { + size_t maxlen = wlen * 4; + char *ret = malloc(maxlen + 1); + int ret_len = WideCharToMultiByte( + CP_UTF8, + 0, + wpath, + wlen, + ret, + maxlen, + NULL, + NULL); + ret[ret_len] = 0; + return ret; +} + + SYS_DIR sys_opendir(const char *path) { struct WinDir *dir = malloc(sizeof(struct WinDir)); @@ -247,7 +326,43 @@ } int sys_lstat(const char *path, SYS_STAT *s) { - return sys_stat(path, s); // unsupported on windows + return sys_stat(path, s); // unsupported on windows +} + +int sys_islink(const char *path) { + // don't use symlinks on windows, because it is not really useful + // however, we interpret .lnk files as symlinks + int ret = 0; + + scstr_t path_s = scstr(path); + if(scstrsuffix(path_s, SC(".lnk"))) { + // looks like a .lnk file + // check content + IShellLink *sl; + HRESULT hres; + hres = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (LPVOID*)&sl); + if(SUCCEEDED(hres)) { + IPersistFile *file; + hres = sl->lpVtbl->QueryInterface(sl, &IID_IPersistFile, (void**)&file); + if(!SUCCEEDED(hres)) { + sl->lpVtbl->Release(sl); + return ret; + } + + int newlen = 0; + wchar_t *wpath = path2winpath(path, 0, &newlen); + + hres = file->lpVtbl->Load(file, wpath, STGM_READ); + if(SUCCEEDED(hres)) { + ret = 1; + file->lpVtbl->Release(file); + } + free(wpath); + + sl->lpVtbl->Release(sl); + } + } + return ret; } int sys_rename(const char *oldpath, const char *newpath) { @@ -292,16 +407,103 @@ return ret; } -ssize_t sys_readlink(const char *path, char *buffer, size_t size) { - // TODO - fprintf(stderr, "sys_readlink: implement me\n"); - return 1; +char* sys_readlink(const char *path, SYS_STAT *s) { + char *ret_link = NULL; + + // create COM object for using the ShellLink interface + IShellLinkW *sl; + HRESULT hres = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (LPVOID*)&sl); + if(!SUCCEEDED(hres)) { + return NULL; + } + + IPersistFile *file; + hres = sl->lpVtbl->QueryInterface(sl, &IID_IPersistFile, (void**)&file); + if(!SUCCEEDED(hres)) { + sl->lpVtbl->Release(sl); + return NULL; + } + + // load .lnk file + int newlen = 0; + wchar_t *wpath = path2winpath(path, 0, &newlen); + hres = file->lpVtbl->Load(file, wpath, STGM_READ); + if(SUCCEEDED(hres)) { + WCHAR link_path[MAX_PATH]; + memset(link_path, 0, MAX_PATH); + + hres = sl->lpVtbl->Resolve(sl, 0, SLR_NO_UI); + if(SUCCEEDED(hres)) { + hres = sl->lpVtbl->GetPath(sl, link_path, MAX_PATH, NULL, SLGP_SHORTPATH); + if(SUCCEEDED(hres)) { + ret_link = winpath2multibyte(link_path, wcslen(link_path)); + } + } + } + // cleanup + free(wpath); + file->lpVtbl->Release(file); + sl->lpVtbl->Release(sl); + + return ret_link; } int sys_symlink(const char *target, const char *linkpath) { - // TODO - fprintf(stderr, "sys_symlink: implement me\n"); - return 1; + // convert relative target to absolut path + char *link_parent = util_parent_path(linkpath); + char *target_unnormalized = util_concat_path(link_parent, target); + char *target_normalized = util_path_normalize(target_unnormalized); + + free(link_parent); + free(target_unnormalized); + + // convert to wchar_t* + int wtargetlen = 0; + wchar_t *wtarget = path2winpath(target_normalized, FALSE, &wtargetlen); + free(target_normalized); + if(!wtarget) { + return 1; + } + + int wlinkpathlen = 0; + wchar_t *wlinkpath = path2winpath(linkpath, FALSE, &wlinkpathlen); + if(!wlinkpath) { + free(wtarget); + return 1; + } + + int ret = 1; + + // create COM object for using the ShellLink interface + IShellLinkW *sl; + HRESULT hres = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (LPVOID*)&sl); + if(SUCCEEDED(hres)) { + IPersistFile *file; + hres = sl->lpVtbl->QueryInterface(sl, &IID_IPersistFile, (void**)&file); + if(SUCCEEDED(hres)) { + // try to load the shortcut + file->lpVtbl->Load(file, wlinkpath, STGM_READ); // ignore error + + // set path + hres = sl->lpVtbl->SetPath(sl, wtarget); + if(SUCCEEDED(hres)) { + hres = file->lpVtbl->Save(file, wlinkpath, TRUE); + if(SUCCEEDED(hres)) { + // successfully created/modified shortcut + ret = 0; // ok + } + } + + file->lpVtbl->Release(file); + } + + sl->lpVtbl->Release(sl); + } + + free(wtarget); + free(wlinkpath); + + return ret; } #endif