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