1 /* Copyright (C) 2019 MariaDB Corporation
2 
3    This program is free software; you can redistribute it and/or
4    modify it under the terms of the GNU General Public License
5    as published by the Free Software Foundation; version 2 of
6    the License.
7 
8    This program is distributed in the hope that it will be useful,
9    but WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11    GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
16    MA 02110-1301, USA. */
17 
18 
19 
20 #include <boost/filesystem.hpp>
21 #include <iostream>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include <time.h>
26 #include "LocalStorage.h"
27 #include "Config.h"
28 
29 using namespace std;
30 namespace bf = boost::filesystem;
31 
32 namespace storagemanager
33 {
34 
LocalStorage()35 LocalStorage::LocalStorage()
36 {
37     prefix = Config::get()->getValue("LocalStorage", "path");
38     //cout << "LS: got prefix " << prefix << endl;
39     if (!bf::is_directory(prefix))
40     {
41         try
42         {
43             bf::create_directories(prefix);
44         }
45         catch (exception &e)
46         {
47             logger->log(LOG_CRIT, "Failed to create %s, got: %s", prefix.string().c_str(), e.what());
48             throw e;
49         }
50     }
51     string stmp = Config::get()->getValue("LocalStorage", "fake_latency");
52     if (!stmp.empty() && (stmp[0] == 'Y' || stmp[0] == 'y'))
53     {
54         fakeLatency = true;
55         stmp = Config::get()->getValue("LocalStorage", "max_latency");
56         usecLatencyCap = strtoull(stmp.c_str(), NULL, 10);
57         if (usecLatencyCap == 0)
58         {
59             logger->log(LOG_CRIT, "LocalStorage:  bad value for max_latency");
60             throw runtime_error("LocalStorage:  bad value for max_latency");
61         }
62         r_seed = (uint) ::time(NULL);
63         logger->log(LOG_DEBUG, "LocalStorage:  Will simulate cloud latency of max %llu us", usecLatencyCap);
64     }
65     else
66         fakeLatency = false;
67 
68     bytesRead = bytesWritten = 0;
69 }
70 
~LocalStorage()71 LocalStorage::~LocalStorage()
72 {
73 }
74 
printKPIs() const75 void LocalStorage::printKPIs() const
76 {
77     cout << "LocalStorage" << endl;
78     cout << "\tbytesRead = " << bytesRead << endl;
79     cout << "\tbytesWritten = " << bytesWritten << endl;
80     CloudStorage::printKPIs();
81 }
82 
getPrefix() const83 const bf::path & LocalStorage::getPrefix() const
84 {
85     return prefix;
86 }
87 
addLatency()88 inline void LocalStorage::addLatency()
89 {
90     if (fakeLatency)
91     {
92         uint64_t usec_delay = ((double) rand_r(&r_seed) / (double) RAND_MAX) * usecLatencyCap;
93         ::usleep(usec_delay);
94     }
95 }
96 
copy(const bf::path & source,const bf::path & dest)97 int LocalStorage::copy(const bf::path &source, const bf::path &dest)
98 {
99     boost::system::error_code err;
100     bf::copy_file(source, dest, bf::copy_option::fail_if_exists, err);
101     if (err)
102     {
103         errno = err.value();
104         ::unlink(dest.string().c_str());
105         return -1;
106     }
107     return 0;
108 }
109 
operator +(const bf::path & p1,const bf::path & p2)110 bf::path operator+(const bf::path &p1, const bf::path &p2)
111 {
112     bf::path ret(p1);
113     ret /= p2;
114     return ret;
115 }
116 
getObject(const string & source,const string & dest,size_t * size)117 int LocalStorage::getObject(const string &source, const string &dest, size_t *size)
118 {
119     addLatency();
120 
121     int ret = copy(prefix / source, dest);
122     if (ret)
123         return ret;
124     size_t _size = bf::file_size(dest);
125     if (size)
126         *size = _size;
127     bytesRead += _size;
128     bytesWritten += _size;
129     ++objectsGotten;
130     return ret;
131 }
132 
getObject(const std::string & sourceKey,boost::shared_array<uint8_t> * data,size_t * size)133 int LocalStorage::getObject(const std::string &sourceKey, boost::shared_array<uint8_t> *data, size_t *size)
134 {
135     addLatency();
136 
137     bf::path source = prefix / sourceKey;
138     const char *c_source = source.string().c_str();
139     //char buf[80];
140     int l_errno;
141 
142     int fd = ::open(c_source, O_RDONLY);
143     if (fd < 0)
144     {
145         l_errno = errno;
146         //logger->log(LOG_WARNING, "LocalStorage::getObject() failed to open %s, got '%s'", c_source, strerror_r(errno, buf, 80));
147         errno = l_errno;
148         return fd;
149     }
150 
151     size_t l_size = bf::file_size(source);
152     data->reset(new uint8_t[l_size]);
153     size_t count = 0;
154     while (count < l_size)
155     {
156         int err = ::read(fd, &(*data)[count], l_size - count);
157         if (err < 0)
158         {
159             l_errno = errno;
160             //logger->log(LOG_WARNING, "LocalStorage::getObject() failed to read %s, got '%s'", c_source, strerror_r(errno, buf, 80));
161             close(fd);
162             bytesRead += count;
163             errno = l_errno;
164             return err;
165         }
166         count += err;
167     }
168     if (size)
169         *size = l_size;
170     close(fd);
171     bytesRead += l_size;
172     ++objectsGotten;
173     return 0;
174 }
175 
putObject(const string & source,const string & dest)176 int LocalStorage::putObject(const string &source, const string &dest)
177 {
178     addLatency();
179 
180     int ret = copy(source, prefix / dest);
181 
182     if (ret == 0)
183     {
184         size_t _size = bf::file_size(source);
185         bytesRead += _size;
186         bytesWritten += _size;
187         ++objectsPut;
188     }
189     return ret;
190 }
191 
putObject(boost::shared_array<uint8_t> data,size_t len,const string & dest)192 int LocalStorage::putObject(boost::shared_array<uint8_t> data, size_t len, const string &dest)
193 {
194     addLatency();
195 
196     bf::path destPath = prefix / dest;
197     const char *c_dest = destPath.string().c_str();
198     //char buf[80];
199     int l_errno;
200 
201     int fd = ::open(c_dest, O_WRONLY | O_CREAT | O_TRUNC, 0600);
202     if (fd < 0)
203     {
204         l_errno = errno;
205         //logger->log(LOG_CRIT, "LocalStorage::putObject(): Failed to open %s, got '%s'", c_dest, strerror_r(errno, buf, 80));
206         errno = l_errno;
207         return fd;
208     }
209 
210     size_t count = 0;
211     int err;
212     while (count < len)
213     {
214         err = ::write(fd, &data[count], len - count);
215         if (err < 0)
216         {
217             l_errno = errno;
218             //logger->log(LOG_CRIT, "LocalStorage::putObject(): Failed to write to %s, got '%s'", c_dest, strerror_r(errno, buf, 80));
219             close(fd);
220             ::unlink(c_dest);
221             errno = l_errno;
222             bytesWritten += count;
223             return err;
224         }
225         count += err;
226     }
227     close(fd);
228     bytesWritten += count;
229     ++objectsPut;
230     return 0;
231 }
232 
copyObject(const string & source,const string & dest)233 int LocalStorage::copyObject(const string &source, const string &dest)
234 {
235     addLatency();
236 
237     int ret = copy(prefix / source, prefix / dest);
238 
239     if (ret == 0)
240     {
241         ++objectsCopied;
242         size_t _size = bf::file_size(prefix/source);
243         bytesRead += _size;
244         bytesWritten += _size;
245     }
246     return ret;
247 }
248 
deleteObject(const string & key)249 int LocalStorage::deleteObject(const string &key)
250 {
251     addLatency();
252 
253     ++objectsDeleted;
254     boost::system::error_code err;
255     bf::remove(prefix / key, err);
256     return 0;
257 }
258 
exists(const std::string & key,bool * out)259 int LocalStorage::exists(const std::string &key, bool *out)
260 {
261     addLatency();
262 
263     ++existenceChecks;
264     *out = bf::exists(prefix / key);
265     return 0;
266 }
267 
268 }
269