1 /*
2  * Copyright 2018 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "file_handler.hpp"
18 
19 #include <cstdint>
20 #include <filesystem>
21 #include <ios>
22 #include <memory>
23 #include <string>
24 #include <vector>
25 
26 namespace ipmi_flash
27 {
28 namespace fs = std::filesystem;
29 
30 bool FileHandler::open(const std::string& path, std::ios_base::openmode mode)
31 {
32     /* force binary mode */
33     mode |= std::ios::binary;
34     this->path = path;
35 
36     if (file.is_open())
37     {
38         /* This wasn't properly closed somehow.
39          * TODO: Throw an error or just reset the state?
40          */
41         return false;
42     }
43 
44     file.open(filename, mode);
45     if (!file.good()) /* on success goodbit is set */
46     {
47         /* TODO: Oh no! Care about this. */
48         return false;
49     }
50 
51     /* We were able to open the file for staging.
52      * TODO: We'll need to do other stuff to eventually.
53      */
54     return true;
55 }
56 
57 void FileHandler::close()
58 {
59     if (file.is_open())
60     {
61         file.close();
62     }
63     return;
64 }
65 
66 bool FileHandler::write(std::uint32_t offset,
67                         const std::vector<std::uint8_t>& data)
68 {
69     if (!file.is_open())
70     {
71         return false;
72     }
73 
74     /* We could track this, but if they write in a scattered method, this is
75      * easier.
76      */
77     file.seekp(offset, std::ios_base::beg);
78     if (!file.good())
79     {
80         /* the documentation wasn't super clear on fail vs bad in these cases,
81          * so let's only be happy with goodness.
82          */
83         return false;
84     }
85 
86     file.write(reinterpret_cast<const char*>(data.data()), data.size());
87     if (!file.good())
88     {
89         return false;
90     }
91 
92     return true;
93 }
94 
95 std::optional<std::vector<uint8_t>> FileHandler::read(std::uint32_t offset,
96                                                       std::uint32_t size)
97 {
98     if (!file.is_open())
99     {
100         return std::nullopt;
101     }
102 
103     /* determine size of file */
104     file.seekg(0, std::ios_base::end);
105     uint32_t filesize = file.tellg();
106     uint32_t bytesToRead = size;
107 
108     /* make sure to not read past the end of file */
109     if (offset + size > filesize)
110     {
111         bytesToRead = filesize - offset;
112     }
113 
114     /* if no bytes can be read, fail */
115     if (0 == bytesToRead)
116     {
117         return std::nullopt;
118     }
119 
120     /* seek to offset then read */
121     file.seekg(offset);
122     std::vector<uint8_t> fileData(bytesToRead);
123     file.read(reinterpret_cast<char*>(fileData.data()), bytesToRead);
124 
125     /* if any sort of failure happened during all the seeks
126      * and reads then fail the entire operation
127      */
128     if (!file.good())
129     {
130         return std::nullopt;
131     }
132     return fileData;
133 }
134 
135 int FileHandler::getSize()
136 {
137     try
138     {
139         return static_cast<int>(fs::file_size(filename));
140     }
141     catch (const fs::filesystem_error& e)
142     {}
143 
144     return 0;
145 }
146 
147 } // namespace ipmi_flash
148