1 /*
2  * Distributed under the OSI-approved Apache License, Version 2.0.  See
3  * accompanying file Copyright.txt for details.
4  *
5  * FileIME.cpp file I/O using IME Native library
6  *
7  *  Created on: Dec 1, 2019
8  *      Author: Keichi Takahashi keichi@is.naist.jp
9  */
10 #include "FileIME.h"
11 
12 #include "adios2/helper/adiosFunctions.h"
13 
14 #include <iostream>
15 
16 #include <fcntl.h>        // O_* flags
17 #include <linux/limits.h> // PATH_MAX
18 #include <unistd.h>       // getcwd
19 
20 extern "C" {
21 #include <im_client_native2.h>
22 }
23 
24 /// \cond EXCLUDE_FROM_DOXYGEN
25 #include <ios> //std::ios_base::failure
26 /// \endcond
27 
28 namespace adios2
29 {
30 namespace transport
31 {
32 
33 std::atomic_uint FileIME::client_refcount = 0;
34 
FileIME(helper::Comm const & comm)35 FileIME::FileIME(helper::Comm const &comm) : Transport("File", "IME", comm)
36 {
37     /** Initialize the IME client if there are no existing FileIME instances.
38      * We need to check if the IME client has been already initialized because
39      * initializing the IME multiple times leaves the client in a weired
40      * state. */
41     if (!client_refcount)
42     {
43         ime_client_native2_init();
44     }
45 
46     client_refcount++;
47 }
48 
~FileIME()49 FileIME::~FileIME()
50 {
51     if (m_IsOpen)
52     {
53         /** Trigger a flush from IME to PFS. Note that fsync needs to be
54          * called before bfs_sync. */
55         if (m_SyncToPFS)
56         {
57             ime_client_native2_fsync(m_FileDescriptor);
58             ime_client_native2_bfs_sync(m_FileDescriptor, true);
59         }
60         ime_client_native2_close(m_FileDescriptor);
61     }
62 
63     client_refcount--;
64 
65     /** Finalize the IME client if there are no existing FileIME instances. */
66     if (client_refcount)
67     {
68         ime_client_native2_finalize();
69     }
70 }
71 
72 /** Note that async mode is unsupported in FileIME. */
Open(const std::string & name,const Mode openMode,const bool async)73 void FileIME::Open(const std::string &name, const Mode openMode,
74                    const bool async)
75 {
76     /** DEFAULT_IME_FILE_PREFIX is "ime://" */
77     m_Name = DEFAULT_IME_FILE_PREFIX;
78 
79     /** Convert relative path to absolute path. The IME API only supports
80      * absolute path. */
81     if (name[0] != '/')
82     {
83         char tmp[PATH_MAX];
84         m_Name += std::string(getcwd(tmp, PATH_MAX)) + "/";
85     }
86     m_Name += name;
87 
88     CheckName();
89     m_OpenMode = openMode;
90     switch (m_OpenMode)
91     {
92     case (Mode::Write):
93         ProfilerStart("open");
94         m_FileDescriptor = ime_client_native2_open(
95             m_Name.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0666);
96         ProfilerStop("open");
97         break;
98 
99     case (Mode::Append):
100         ProfilerStart("open");
101         m_FileDescriptor =
102             ime_client_native2_open(m_Name.c_str(), O_RDWR | O_CREAT, 0777);
103         lseek(m_FileDescriptor, 0, SEEK_END);
104         ProfilerStop("open");
105         break;
106 
107     case (Mode::Read):
108         ProfilerStart("open");
109         m_FileDescriptor =
110             ime_client_native2_open(m_Name.c_str(), O_RDONLY, 0000);
111         ProfilerStop("open");
112         break;
113 
114     default:
115         CheckFile("unknown open mode for file " + m_Name +
116                   ", in call to IME open");
117     }
118 
119     CheckFile("couldn't open file " + m_Name +
120               ", check permissions or path existence, in call to IME open");
121 
122     m_IsOpen = true;
123 }
124 
SetParameters(const Params & parameters)125 void FileIME::SetParameters(const Params &parameters)
126 {
127     for (const auto &pair : parameters)
128     {
129         const std::string key = helper::LowerCase(pair.first);
130         const std::string value = helper::LowerCase(pair.second);
131 
132         if (key == "synctopfs")
133         {
134             m_SyncToPFS =
135                 helper::StringTo<bool>(value, " in Parameter key=SyncToPFS");
136         }
137     }
138 }
139 
Write(const char * buffer,size_t size,size_t start)140 void FileIME::Write(const char *buffer, size_t size, size_t start)
141 {
142     auto lf_Write = [&](const char *buffer, size_t size) {
143         while (size > 0)
144         {
145             ProfilerStart("write");
146             const auto writtenSize =
147                 ime_client_native2_write(m_FileDescriptor, buffer, size);
148             ProfilerStop("write");
149 
150             if (writtenSize == -1)
151             {
152                 if (errno == EINTR)
153                 {
154                     continue;
155                 }
156 
157                 throw std::ios_base::failure(
158                     "ERROR: couldn't write to file " + m_Name +
159                     ", in call to FileDescriptor Write\n");
160             }
161 
162             buffer += writtenSize;
163             size -= writtenSize;
164         }
165     };
166 
167     if (start != MaxSizeT)
168     {
169         const auto newPosition =
170             ime_client_native2_lseek(m_FileDescriptor, start, SEEK_SET);
171 
172         if (static_cast<size_t>(newPosition) != start)
173         {
174             throw std::ios_base::failure(
175                 "ERROR: couldn't move to start position " +
176                 std::to_string(start) + " in file " + m_Name +
177                 ", in call to IME lseek\n");
178         }
179     }
180 
181     if (size > DefaultMaxFileBatchSize)
182     {
183         const size_t batches = size / DefaultMaxFileBatchSize;
184         const size_t remainder = size % DefaultMaxFileBatchSize;
185 
186         size_t position = 0;
187         for (size_t b = 0; b < batches; ++b)
188         {
189             lf_Write(&buffer[position], DefaultMaxFileBatchSize);
190             position += DefaultMaxFileBatchSize;
191         }
192         lf_Write(&buffer[position], remainder);
193     }
194     else
195     {
196         lf_Write(buffer, size);
197     }
198 }
199 
Read(char * buffer,size_t size,size_t start)200 void FileIME::Read(char *buffer, size_t size, size_t start)
201 {
202     auto lf_Read = [&](char *buffer, size_t size) {
203         while (size > 0)
204         {
205             ProfilerStart("read");
206             const auto readSize =
207                 ime_client_native2_read(m_FileDescriptor, buffer, size);
208             ProfilerStop("read");
209 
210             if (readSize == -1)
211             {
212                 if (errno == EINTR)
213                 {
214                     continue;
215                 }
216 
217                 throw std::ios_base::failure("ERROR: couldn't read from file " +
218                                              m_Name +
219                                              ", in call to IME IO read\n");
220             }
221 
222             buffer += readSize;
223             size -= readSize;
224         }
225     };
226 
227     if (start != MaxSizeT)
228     {
229         const auto newPosition =
230             ime_client_native2_lseek(m_FileDescriptor, start, SEEK_SET);
231 
232         if (static_cast<size_t>(newPosition) != start)
233         {
234             throw std::ios_base::failure(
235                 "ERROR: couldn't move to start position " +
236                 std::to_string(start) + " in file " + m_Name +
237                 ", in call to IME lseek errno " + std::to_string(errno) + "\n");
238         }
239     }
240 
241     if (size > DefaultMaxFileBatchSize)
242     {
243         const size_t batches = size / DefaultMaxFileBatchSize;
244         const size_t remainder = size % DefaultMaxFileBatchSize;
245 
246         size_t position = 0;
247         for (size_t b = 0; b < batches; ++b)
248         {
249             lf_Read(&buffer[position], DefaultMaxFileBatchSize);
250             position += DefaultMaxFileBatchSize;
251         }
252         lf_Read(&buffer[position], remainder);
253     }
254     else
255     {
256         lf_Read(buffer, size);
257     }
258 }
259 
GetSize()260 size_t FileIME::GetSize()
261 {
262     struct stat fileStat;
263     if (fstat(m_FileDescriptor, &fileStat) == -1)
264     {
265         throw std::ios_base::failure("ERROR: couldn't get size of file " +
266                                      m_Name + "\n");
267     }
268     return static_cast<size_t>(fileStat.st_size);
269 }
270 
Flush()271 void FileIME::Flush()
272 {
273     if (m_SyncToPFS)
274     {
275         ime_client_native2_fsync(m_FileDescriptor);
276         ime_client_native2_bfs_sync(m_FileDescriptor, true);
277     }
278 }
279 
Close()280 void FileIME::Close()
281 {
282     ProfilerStart("close");
283     if (m_SyncToPFS)
284     {
285         ime_client_native2_fsync(m_FileDescriptor);
286         ime_client_native2_bfs_sync(m_FileDescriptor, true);
287     }
288     const int status = ime_client_native2_close(m_FileDescriptor);
289     ProfilerStop("close");
290 
291     if (status == -1)
292     {
293         throw std::ios_base::failure("ERROR: couldn't close file " + m_Name +
294                                      ", in call to IME IO close\n");
295     }
296 
297     m_IsOpen = false;
298 }
299 
Delete()300 void FileIME::Delete()
301 {
302     if (m_IsOpen)
303     {
304         Close();
305     }
306     ime_client_native2_unlink(m_Name.c_str());
307 }
308 
CheckFile(const std::string hint) const309 void FileIME::CheckFile(const std::string hint) const
310 {
311     if (m_FileDescriptor == -1)
312     {
313         throw std::ios_base::failure("ERROR: " + hint + "\n");
314     }
315 }
316 
SeekToEnd()317 void FileIME::SeekToEnd()
318 {
319     const int status = ime_client_native2_lseek(m_FileDescriptor, 0, SEEK_END);
320     if (status == -1)
321     {
322         throw std::ios_base::failure(
323             "ERROR: couldn't seek to the end of file " + m_Name +
324             ", in call to IME IO lseek\n");
325     }
326 }
327 
SeekToBegin()328 void FileIME::SeekToBegin()
329 {
330     const int status = ime_client_native2_lseek(m_FileDescriptor, 0, SEEK_SET);
331     if (status == -1)
332     {
333         throw std::ios_base::failure(
334             "ERROR: couldn't seek to the begin of file " + m_Name +
335             ", in call to IME IO lseek\n");
336     }
337 }
338 
339 } // end namespace transport
340 } // end namespace adios2
341