UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2024 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <libidav/utils.h> 30 31 #include <stdio.h> 32 #include <stdlib.h> 33 #include <string.h> 34 #include <sys/stat.h> 35 #include <sys/types.h> 36 #include <errno.h> 37 38 #include <cx/string.h> 39 40 #ifndef _WIN32 41 #include <unistd.h> 42 #endif 43 44 #include "system.h" 45 46 void sys_freedirent(SysDirEnt *ent) { 47 free(ent->name); 48 free(ent); 49 } 50 51 #ifndef _WIN32 52 /* ---------- POSIX implementation ---------- */ 53 54 void sys_init(void) { 55 56 } 57 void sys_uninit(void) { 58 59 } 60 61 SYS_DIR sys_opendir(const char *path) { 62 DIR *dir = opendir(path); 63 if(!dir) { 64 return NULL; 65 } 66 SysDir *d = malloc(sizeof(SysDir)); 67 d->dir = dir; 68 d->ent = NULL; 69 return d; 70 } 71 72 SysDirEnt* sys_readdir(SYS_DIR dir) { 73 if(dir->ent) { 74 free(dir->ent->name); 75 free(dir->ent); 76 dir->ent = NULL; 77 } 78 struct dirent *ent = readdir(dir->dir); 79 if(ent) { 80 SysDirEnt *e = malloc(sizeof(SysDirEnt)); 81 e->name = strdup(ent->d_name); 82 dir->ent = e; 83 return e; 84 } 85 return NULL; 86 } 87 88 void sys_closedir(SYS_DIR dir) { 89 closedir(dir->dir); 90 if(dir->ent) { 91 free(dir->ent->name); 92 free(dir->ent); 93 } 94 free(dir); 95 } 96 97 FILE* sys_fopen(const char *path, const char *mode) { 98 return fopen(path, mode); 99 } 100 101 int sys_stat(const char *path, SYS_STAT *s) { 102 return stat(path, s); 103 } 104 105 int sys_lstat(const char *path, SYS_STAT *s) { 106 return lstat(path, s); 107 } 108 109 int sys_islink(const char *path) { 110 struct stat s; 111 if(!lstat(path, &s)) { 112 return S_ISLNK(s.st_mode); 113 } 114 return 0; 115 } 116 117 int sys_rename(const char *oldpath, const char *newpath) { 118 return rename(oldpath, newpath); 119 } 120 121 int sys_unlink(const char *path) { 122 return unlink(path); 123 } 124 125 int sys_mkdir(const char *path) { 126 return mkdir(path, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); 127 } 128 129 char* sys_readlink(const char *path, SYS_STAT *s) { 130 char *ret = NULL; 131 132 off_t l_sz = s->st_size + 16; 133 size_t lnksize = l_sz > 256 ? l_sz : 256; 134 char *lnkbuf = malloc(lnksize); 135 136 ssize_t len = 0; 137 for(int i=0;i<4;i++) { 138 // we try to read the link at most 4 times 139 // only repeat if the buffer is too small 140 len = readlink(path, lnkbuf, lnksize); 141 if(len < lnksize) { 142 ret = lnkbuf; // success 143 lnkbuf[len] = 0; // terminate buffer 144 break; 145 } 146 lnksize *= 2; // retry with bigger buffer 147 lnkbuf = realloc(lnkbuf, lnksize); 148 } 149 150 if(!ret) { 151 free(lnkbuf); 152 } 153 return ret; 154 } 155 156 int sys_symlink(const char *target, const char *linkpath) { 157 int err = symlink(target, linkpath); 158 if(err && errno == EEXIST) { 159 if(unlink(linkpath)) { 160 return 1; 161 } 162 return sys_symlink(target, linkpath); 163 } 164 return err; 165 } 166 167 int sys_truncate(const char* path, off_t length) { 168 return truncate(path, length); 169 } 170 171 #else 172 /* ---------- Windows implementation ---------- */ 173 174 #include <windows.h> 175 #include <winnls.h> 176 #include <shobjidl.h> 177 #include <objbase.h> 178 #include <objidl.h> 179 180 #include <direct.h> 181 #include <wchar.h> 182 183 void sys_init(void) { 184 HRESULT res = CoInitialize(NULL); 185 if(res != S_OK) { 186 fprintf(stderr, "Error: CoInitialize failed\n"); 187 } 188 } 189 190 void sys_uninit(void) { 191 CoUninitialize(); 192 } 193 194 static wchar_t* path2winpath(const char *path, int dir, int *newlen) { 195 size_t len = strlen(path); 196 size_t lenadd = dir ? 2 : 0; 197 198 199 wchar_t *wpath = calloc(len+lenadd+1, sizeof(wchar_t)); 200 int wlen = MultiByteToWideChar( 201 CP_UTF8, 202 0, 203 path, 204 len, 205 wpath, 206 len+1 207 ); 208 if(newlen) { 209 *newlen = wlen; 210 } 211 for(int i=0;i<wlen;i++) { 212 if(wpath[i] == '/'L) { 213 wpath[i] = '\\'L; 214 } 215 } 216 217 if(dir) { 218 if(wpath[wlen-1] != '\\'L) { 219 wpath[wlen++] = '\\'L; 220 } 221 wpath[wlen++] = '*'L; 222 } 223 wpath[wlen] = 0; 224 225 return wpath; 226 } 227 228 static char* winpath2multibyte(const wchar_t *wpath, size_t wlen) { 229 size_t maxlen = wlen * 4; 230 char *ret = malloc(maxlen + 1); 231 int ret_len = WideCharToMultiByte( 232 CP_UTF8, 233 0, 234 wpath, 235 wlen, 236 ret, 237 maxlen, 238 NULL, 239 NULL); 240 ret[ret_len] = 0; 241 return ret; 242 } 243 244 245 246 SYS_DIR sys_opendir(const char *path) { 247 struct WinDir *dir = malloc(sizeof(struct WinDir)); 248 wchar_t *dirpath = path2winpath(path, TRUE, NULL); 249 if(!dirpath) { 250 fprintf(stderr, "Cannot convert path \"%s\" to UTF16\n", path); 251 free(dir); 252 return NULL; 253 } 254 dir->first = 1; 255 dir->handle = FindFirstFileW(dirpath, &dir->finddata); 256 free(dirpath); 257 if(dir->handle == INVALID_HANDLE_VALUE) { 258 free(dir); 259 return NULL; 260 } 261 dir->ent = NULL; 262 return dir; 263 } 264 265 SysDirEnt* sys_readdir(SYS_DIR dir) { 266 if(dir->ent) { 267 free(dir->ent->name); 268 free(dir->ent); 269 dir->ent = NULL; 270 } 271 if(dir->first) { 272 dir->first = 0; 273 } else { 274 if(FindNextFileW(dir->handle, &dir->finddata) == 0) { 275 return NULL; 276 } 277 } 278 279 size_t namelen = wcslen(dir->finddata.cFileName); 280 281 char *name = malloc((namelen+1)*4); 282 int nlen = WideCharToMultiByte( 283 CP_UTF8, 284 0, 285 dir->finddata.cFileName, 286 -1, 287 name, 288 256, 289 NULL, 290 NULL); 291 if(nlen > 0) { 292 name[nlen] = 0; 293 SysDirEnt *ent = malloc(sizeof(SysDirEnt)); 294 ent->name = name; 295 dir->ent = ent; 296 return ent; 297 } else { 298 return NULL; 299 } 300 } 301 302 void sys_closedir(SYS_DIR dir) { 303 if(dir->ent) { 304 free(dir->ent->name); 305 free(dir->ent); 306 } 307 FindClose(dir->handle); 308 free(dir); 309 } 310 311 FILE* sys_fopen(const char *path, const char *mode) { 312 wchar_t *fpath = path2winpath(path, FALSE, NULL); 313 wchar_t *fmode = path2winpath(mode, FALSE, NULL); 314 315 FILE *file = (fpath && fmode) ? _wfopen(fpath, fmode) : NULL; 316 free(fpath); 317 free(fmode); 318 return file; 319 } 320 321 int sys_stat(const char *path, SYS_STAT *s) { 322 wchar_t *fpath = path2winpath(path, FALSE, NULL); 323 if(!fpath) { 324 fprintf(stderr, "Cannot convert path \"%s\" to UTF16\n", path); 325 return -1; 326 } 327 int ret = _wstat64(fpath, s); 328 free(fpath); 329 return ret; 330 } 331 332 int sys_lstat(const char *path, SYS_STAT *s) { 333 return sys_stat(path, s); // unsupported on windows 334 } 335 336 int sys_islink(const char *path) { 337 // don't use symlinks on windows, because it is not really useful 338 // however, we interpret .lnk files as symlinks 339 int ret = 0; 340 341 cxstring path_s = cx_str(path); 342 if(cx_strsuffix(path_s, CX_STR(".lnk"))) { 343 // looks like a .lnk file 344 // check content 345 IShellLink *sl; 346 HRESULT hres; 347 hres = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (LPVOID*)&sl); 348 if(SUCCEEDED(hres)) { 349 IPersistFile *file; 350 hres = sl->lpVtbl->QueryInterface(sl, &IID_IPersistFile, (void**)&file); 351 if(!SUCCEEDED(hres)) { 352 sl->lpVtbl->Release(sl); 353 return ret; 354 } 355 356 int newlen = 0; 357 wchar_t *wpath = path2winpath(path, 0, &newlen); 358 359 hres = file->lpVtbl->Load(file, wpath, STGM_READ); 360 if(SUCCEEDED(hres)) { 361 ret = 1; 362 file->lpVtbl->Release(file); 363 } 364 free(wpath); 365 366 sl->lpVtbl->Release(sl); 367 } 368 } 369 return ret; 370 } 371 372 int sys_rename(const char *oldpath, const char *newpath) { 373 wchar_t *o = path2winpath(oldpath, FALSE, NULL); 374 wchar_t *n = path2winpath(newpath, FALSE, NULL); 375 if(!o || !n) { 376 return -1; 377 } 378 379 struct __stat64 s; 380 if(!_wstat64(n, &s)) { 381 if(_wunlink(n)) { 382 fprintf(stderr, "sys_rename: cannot delete existing file: %ls\n", n); 383 } 384 } 385 386 int ret = _wrename(o, n); 387 free(o); 388 free(n); 389 return ret; 390 } 391 392 int sys_unlink(const char *path) { 393 wchar_t *wpath = path2winpath(path, FALSE, NULL); 394 if(!wpath) { 395 fprintf(stderr, "sys_unlink: cannot convert path\n"); 396 return -1; 397 } 398 int ret = _wunlink(wpath); 399 free(wpath); 400 return ret; 401 } 402 403 int sys_mkdir(const char *path) { 404 wchar_t *wpath = path2winpath(path, FALSE, NULL); 405 if(!wpath) { 406 fprintf(stderr, "sys_mkdir: cannot convert path\n"); 407 return -1; 408 } 409 int ret = _wmkdir(wpath); 410 free(wpath); 411 return ret; 412 } 413 414 char* sys_readlink(const char *path, SYS_STAT *s) { 415 char *ret_link = NULL; 416 417 // create COM object for using the ShellLink interface 418 IShellLinkW *sl; 419 HRESULT hres = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (LPVOID*)&sl); 420 if(!SUCCEEDED(hres)) { 421 return NULL; 422 } 423 424 IPersistFile *file; 425 hres = sl->lpVtbl->QueryInterface(sl, &IID_IPersistFile, (void**)&file); 426 if(!SUCCEEDED(hres)) { 427 sl->lpVtbl->Release(sl); 428 return NULL; 429 } 430 431 // load .lnk file 432 int newlen = 0; 433 wchar_t *wpath = path2winpath(path, 0, &newlen); 434 hres = file->lpVtbl->Load(file, wpath, STGM_READ); 435 if(SUCCEEDED(hres)) { 436 WCHAR link_path[MAX_PATH]; 437 memset(link_path, 0, MAX_PATH); 438 439 hres = sl->lpVtbl->Resolve(sl, 0, SLR_NO_UI); 440 if(SUCCEEDED(hres)) { 441 hres = sl->lpVtbl->GetPath(sl, link_path, MAX_PATH, NULL, SLGP_SHORTPATH); 442 if(SUCCEEDED(hres)) { 443 ret_link = winpath2multibyte(link_path, wcslen(link_path)); 444 } 445 } 446 } 447 // cleanup 448 free(wpath); 449 file->lpVtbl->Release(file); 450 sl->lpVtbl->Release(sl); 451 452 return ret_link; 453 } 454 455 int sys_symlink(const char *target, const char *linkpath) { 456 // convert relative target to absolut path 457 char *link_parent = util_parent_path(linkpath); 458 char *target_unnormalized = util_concat_path(link_parent, target); 459 char *target_normalized = util_path_normalize(target_unnormalized); 460 461 free(link_parent); 462 free(target_unnormalized); 463 464 // convert to wchar_t* 465 int wtargetlen = 0; 466 wchar_t *wtarget = path2winpath(target_normalized, FALSE, &wtargetlen); 467 free(target_normalized); 468 if(!wtarget) { 469 return 1; 470 } 471 472 int wlinkpathlen = 0; 473 wchar_t *wlinkpath = path2winpath(linkpath, FALSE, &wlinkpathlen); 474 if(!wlinkpath) { 475 free(wtarget); 476 return 1; 477 } 478 479 int ret = 1; 480 481 // create COM object for using the ShellLink interface 482 IShellLinkW *sl; 483 HRESULT hres = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (LPVOID*)&sl); 484 if(SUCCEEDED(hres)) { 485 IPersistFile *file; 486 hres = sl->lpVtbl->QueryInterface(sl, &IID_IPersistFile, (void**)&file); 487 if(SUCCEEDED(hres)) { 488 // try to load the shortcut 489 file->lpVtbl->Load(file, wlinkpath, STGM_READ); // ignore error 490 491 // set path 492 hres = sl->lpVtbl->SetPath(sl, wtarget); 493 if(SUCCEEDED(hres)) { 494 hres = file->lpVtbl->Save(file, wlinkpath, TRUE); 495 if(SUCCEEDED(hres)) { 496 // successfully created/modified shortcut 497 ret = 0; // ok 498 } 499 } 500 501 file->lpVtbl->Release(file); 502 } 503 504 sl->lpVtbl->Release(sl); 505 } 506 507 free(wtarget); 508 free(wlinkpath); 509 510 return ret; 511 } 512 513 int sys_truncate(const char* path, off_t length) { 514 wchar_t* wpath = path2winpath(path, FALSE, NULL); 515 if (!wpath) { 516 fprintf(stderr, "sys_truncate: cannot convert path\n"); 517 return -1; 518 } 519 520 FILE* file = _wfopen(wpath, "wb"L); 521 int ret = 1; 522 if (file) { 523 ret = _chsize(fileno(file), length); 524 fclose(file); 525 } 526 527 free(wpath); 528 return ret; 529 } 530 531 #endif 532