1 /*
2   Copyright 2021 Northern.tech AS
3 
4   This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5 
6   This program is free software; you can redistribute it and/or modify it
7   under the terms of the GNU General Public License as published by the
8   Free Software Foundation; version 3.
9 
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14 
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
18 
19   To the extent this program is licensed as part of the Enterprise
20   versions of CFEngine, the applicable Commercial Open Source License
21   (COSL) may apply to this file if you as a licensee so wish it. See
22   included file COSL.txt.
23 */
24 
25 #include <cf3.defs.h>
26 
27 #include <files_names.h>
28 #include <files_interfaces.h>
29 #include <files_lib.h>
30 #include <files_copy.h>
31 #include <item_lib.h>
32 #include <mutex.h>
33 #include <global_mutex.h>
34 #include <policy.h>
35 #include <string_lib.h>                                       /* PathAppend */
36 
37 /*********************************************************************/
38 
39 static Item *VREPOSLIST = NULL; /* GLOBAL_X */
40 static char REPOSCHAR = '_'; /* GLOBAL_P */
41 static char *VREPOSITORY = NULL; /* GLOBAL_P */
42 
43 /*********************************************************************/
44 
SetRepositoryLocation(const char * path)45 void SetRepositoryLocation(const char *path)
46 {
47     VREPOSITORY = xstrdup(path);
48 }
49 
50 /*********************************************************************/
51 
SetRepositoryChar(char c)52 void SetRepositoryChar(char c)
53 {
54     REPOSCHAR = c;
55 }
56 
57 /*********************************************************************/
58 
GetRepositoryPath(ARG_UNUSED const char * file,const Attributes * attr,char * destination)59 bool GetRepositoryPath(ARG_UNUSED const char *file, const Attributes *attr, char *destination)
60 {
61     if ((attr->repository == NULL) && (VREPOSITORY == NULL))
62     {
63         return false;
64     }
65 
66     size_t repopathlen;
67 
68     if (attr->repository != NULL)
69     {
70         repopathlen = strlcpy(destination, attr->repository, CF_BUFSIZE);
71     }
72     else
73     {
74         repopathlen = strlcpy(destination, VREPOSITORY, CF_BUFSIZE);
75     }
76 
77     if (repopathlen >= CF_BUFSIZE)
78     {
79         Log(LOG_LEVEL_ERR, "Internal limit, buffer ran out of space for long filename");
80         return false;
81     }
82 
83     return true;
84 }
85 
86 /*********************************************************************/
87 
ArchiveToRepository(const char * file,const Attributes * attr)88 bool ArchiveToRepository(const char *file, const Attributes *attr)
89  /* Returns true if the file was backup up and false if not */
90 {
91     char destination[CF_BUFSIZE];
92     struct stat sb, dsb;
93 
94     // Skip empty file name
95     if (file[0] == '\0') {
96         return false;
97     }
98 
99     if (!GetRepositoryPath(file, attr, destination))
100     {
101         return false;
102     }
103 
104     if (attr->copy.backup == BACKUP_OPTION_NO_BACKUP)
105     {
106         return true;
107     }
108 
109     if (IsItemIn(VREPOSLIST, file))
110     {
111         Log(LOG_LEVEL_INFO,
112             "The file '%s' has already been moved to the repository once. Multiple update will cause loss of backup.",
113               file);
114         return true;
115     }
116 
117     ThreadLock(cft_getaddr);
118     PrependItemList(&VREPOSLIST, file);
119     ThreadUnlock(cft_getaddr);
120 
121     if (!PathAppend(destination, sizeof(destination),
122                     CanonifyName(file), FILE_SEPARATOR))
123     {
124         Log(LOG_LEVEL_ERR,
125             "Internal limit reached in ArchiveToRepository(),"
126             " path too long: '%s' + '%s'",
127             destination, CanonifyName(file));
128         return false;
129     }
130 
131     if (!MakeParentDirectory(destination, attr->move_obstructions, NULL))
132     {
133         // Could not create parent directory, assume this is okay,
134         // verbose logging in MakeParentDirectory()
135         Log(LOG_LEVEL_DEBUG,
136             "Could not create parent directory '%s'",
137             destination);
138     }
139 
140     if (stat(file, &sb) == -1)
141     {
142         Log(LOG_LEVEL_DEBUG, "File '%s' promised to archive to the repository but it disappeared!", file);
143         return true;
144     }
145 
146     stat(destination, &dsb);
147 
148     if (CopyRegularFileDisk(file, destination))
149     {
150         Log(LOG_LEVEL_INFO, "Moved '%s' to repository location '%s'", file, destination);
151         return true;
152     }
153     else
154     {
155         Log(LOG_LEVEL_INFO, "Failed to move '%s' to repository location '%s'", file, destination);
156         return false;
157     }
158 }
159 
FileInRepository(const char * filename)160 bool FileInRepository(const char *filename)
161 {
162     return IsItemIn(VREPOSLIST, filename);
163 }
164