1 /*
2 * Copyright (C) 2021 Alexandros Theodotou <alex at zrythm dot org>
3 *
4 * This file is part of Zrythm
5 *
6 * Zrythm is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Affero General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * Zrythm 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
14 * GNU Affero General Public License for more details.
15 *
16 * You should have received a copy of the GNU Affero General Public License
17 * along with Zrythm. If not, see <https://www.gnu.org/licenses/>.
18 *
19 * This file incorporates work covered by the following copyright and
20 * permission notice:
21 *
22 * Copyright 2007-2021 David Robillard <d@drobilla.net>
23 *
24 * Permission to use, copy, modify, and/or distribute this software for any
25 * purpose with or without fee is hereby granted, provided that the above
26 * copyright notice and this permission notice appear in all copies.
27 *
28 * THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
29 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
30 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
31 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
32 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
33 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
34 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
35 */
36
37 #include "zrythm-config.h"
38
39 #include <errno.h>
40 #include <stdbool.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #ifdef __linux__
44 #include <fcntl.h>
45 #include <linux/fs.h>
46 #include <sys/ioctl.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #endif
50
51 #ifdef _WOE32
52 #include <windows.h>
53 #else
54 #include <unistd.h>
55 #endif
56
57 #include "utils/file.h"
58
59 #include <glib.h>
60 #include <glib/gstdio.h>
61
62 char *
file_path_relative_to(const char * path,const char * base)63 file_path_relative_to (
64 const char * path,
65 const char * base)
66 {
67 const size_t path_len = strlen(path);
68 const size_t base_len = strlen(base);
69 const size_t min_len = (path_len < base_len) ? path_len : base_len;
70
71 // Find the last separator common to both paths
72 size_t last_shared_sep = 0;
73 for (size_t i = 0; i < min_len && path[i] == base[i]; ++i) {
74 if (path[i] == G_DIR_SEPARATOR) {
75 last_shared_sep = i;
76 }
77 }
78
79 if (last_shared_sep == 0) {
80 // No common components, return path
81 return g_strdup(path);
82 }
83
84 // Find the number of up references ("..") required
85 size_t up = 0;
86 for (size_t i = last_shared_sep + 1; i < base_len; ++i) {
87 if (base[i] == G_DIR_SEPARATOR) {
88 ++up;
89 }
90 }
91
92 #ifdef _WOE32
93 const bool use_slash = strchr(path, '/');
94 #else
95 static const bool use_slash = true;
96 #endif
97
98 // Write up references
99 const size_t suffix_len = path_len - last_shared_sep;
100 char * rel =
101 g_malloc0_n (1, suffix_len + (up * 3) + 1);
102 for (size_t i = 0; i < up; ++i) {
103 if (use_slash) {
104 memcpy(rel + (i * 3), "../", 3);
105 } else {
106 memcpy(rel + (i * 3), "..\\", 3);
107 }
108 }
109
110 // Write suffix
111 memcpy(rel + (up * 3), path + last_shared_sep + 1, suffix_len);
112 return rel;
113 }
114
115 int
file_symlink(const char * old_path,const char * new_path)116 file_symlink (
117 const char * old_path,
118 const char * new_path)
119 {
120 int ret = 0;
121 #ifdef _WOE32
122 ret =
123 !CreateHardLink (
124 new_path, old_path, 0);
125 #else
126 char * target =
127 file_path_relative_to (
128 old_path, new_path);
129 ret = symlink (target, new_path);
130 #endif
131
132 return ret;
133 }
134
135 /**
136 * Do cp --reflink from \ref src to \ref dest.
137 *
138 * @return Non-zero on error.
139 */
140 int
file_reflink(const char * dest,const char * src)141 file_reflink (
142 const char * dest,
143 const char * src)
144 {
145 #ifdef __linux__
146 int src_fd = g_open (src, O_RDONLY);
147 if (src_fd)
148 return src_fd;
149 int dest_fd =
150 g_open (dest, O_RDWR | O_CREAT, 0644);
151 if (dest_fd)
152 return src_fd;
153 return ioctl (dest_fd, FICLONE, src_fd);
154 #else
155 return -1;
156 #endif
157 }
158