1 /*
  2  * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 #include <map>
 26 #include <mutex>
 27 #include <iostream>
 28 #include <sstream>
 29 #include <fstream>
 30 #include <dirent.h>
 31 #include <unistd.h>
 32 #include <sys/stat.h>
 33 #include <fcntl.h>
 34 #include "fsutil.h"
 35 #include "strutil.h"
 36 //#define TRACEMEM
 37 #ifdef TRACEMEM
 38 
 39 struct item{
 40    const char * tag;
 41    void *ptr;
 42    size_t size;
 43 };
 44 #define HEAP 100000
 45 #define HEAPSIZE (HEAP*sizeof(item))
 46 static item *items = new item[HEAP];
 47 static int itemc =0;
 48 static long heapUsage = 0;
 49 const char *grabTag = "general";
 50 
 51 std::recursive_mutex  heapRecursiveMutex;
 52 
 53 void lockheap() {
 54    // std::cerr << "about to acquire lock " << index(std::this_thread::get_id()) << std::endl;
 55    while (!heapRecursiveMutex.try_lock()) {
 56       //   std::cerr << "Looks like we are contended!" << std::endl;
 57       //  std::cerr.flush();
 58       ::usleep(1000);
 59    }
 60    // recursiveMutex.lock();
 61    // std::cerr << "got lock " << index(std::this_thread::get_id()) << std::endl;
 62 
 63 }
 64 
 65 void unlockheap() {
 66    heapRecursiveMutex.unlock();
 67    //std::cerr << "just released lock " << index(std::this_thread::get_id()) << std::endl;
 68 }
 69 
 70 void *grab(const char *tag, size_t bytes){
 71 
 72    if (bytes == HEAPSIZE){
 73       return malloc(bytes);
 74    }
 75 
 76    if (itemc == 0){
 77       ::memset(items, 0, HEAPSIZE);
 78    }
 79    lockheap();
 80    items[itemc].size =bytes;
 81    items[itemc].tag =tag;
 82    heapUsage+=bytes;
 83  //  std::cout << "allocating slot "<<itemc<< " " << bytes<< " from "<< tag << std::endl;
 84    void *ptr = items[itemc++].ptr =malloc(bytes);
 85    unlockheap();
 86    return ptr;
 87 }
 88 
 89 void * operator new(size_t bytes){
 90    return grab(grabTag, bytes);
 91 }
 92 void operator delete(void *bytes){
 93 
 94    for (int i=0; i< itemc; i++){
 95       if (items[i].ptr == bytes){
 96        //  std::cout << "freeing "<<items[i].size<<" from "<<i<< " " << items[i].tag << std::endl;
 97          lockheap();
 98          heapUsage-=items[i].size;
 99          free(bytes);
100          items[i].ptr = nullptr;
101          items[i].size=0;
102 
103          unlockheap();
104          std::cout << "delta "<<heapUsage<< std::endl;
105          return;
106       }
107    }
108 
109       std::cout << "no allocation for "<< (long)bytes<<std::endl;
110 }
111 #endif
112 void fsutil::visit(const std::string &dirName, bool recurse, std::function<void(bool dir, std::string name)> visitor) {
113     DIR *d;
114     if ((d = opendir(dirName.c_str())) != nullptr) {
115         struct dirent *ent;
116         while ((ent = readdir(d)) != nullptr) {
117             std::string name = dirName + "/" + ent->d_name;
118             if (ent->d_type & DT_REG) {
119                 visitor(false, name);
120             } else if (std::strcmp(ent->d_name, ".") != 0 && std::strcmp(ent->d_name, "..") != 0 &&
121                        ent->d_type & DT_DIR) {
122                 visitor(true, name);
123                 if (recurse) {
124                     visit(name, recurse, visitor);
125                 }
126             }
127         }
128         closedir(d);
129     }
130 }
131 
132 void fsutil::forEachFileName(const std::string &dirName, std::function<void(std::string name)> visitor) {
133     DIR *d;
134     if ((d = opendir(dirName.c_str())) != nullptr) {
135         struct dirent *ent;
136         while ((ent = readdir(d)) != nullptr) {
137             std::string name = dirName + "/" + ent->d_name;
138             if (ent->d_type & DT_REG) {
139                 visitor(name);
140             }
141         }
142         closedir(d);
143     }
144 }
145 
146 void fsutil::forEachDirName(const std::string &dirName, std::function<void(std::string name)> visitor) {
147     DIR *d;
148     if ((d = opendir(dirName.c_str())) != nullptr) {
149         struct dirent *ent;
150         while ((ent = readdir(d)) != nullptr) {
151             std::string name = dirName + "/" + ent->d_name;
152             if (ent->d_type & DT_DIR && std::strcmp(ent->d_name, ".") != 0 && std::strcmp(ent->d_name, "..") != 0) {
153                 visitor(name);
154             }
155             closedir(d);
156         }
157     }
158 }
159 
160 void fsutil::forEachLine(const std::string &fileName, std::function<void(std::string name)> visitor) {
161     std::size_t current, previous = 0;
162     std::string content = getFile(fileName);
163     current = content.find('\n');
164     while (current != std::string::npos) {
165         visitor(std::string(content, previous, current - previous));
166         previous = current + 1;
167         current = content.find('\n', previous);
168     }
169 }
170 
171 #define BUF_SIZE 4096
172 
173 void fsutil::send(int from, size_t bytes, int to) {
174     char buf[BUF_SIZE];
175     size_t bytesRead;
176     size_t totalSent = 0;
177     size_t bytesSent;
178     while (bytes > 0
179            && (((bytesRead = read(from, buf, ((bytes < BUF_SIZE) ? bytes : BUF_SIZE)))) > 0)
180            && (((bytesSent = ::write(to, buf, bytesRead))) > 0)) {
181         bytes -= bytesRead;
182         totalSent += bytesRead;
183     }
184     if (bytesSent == 0) {
185         perror("sendfile: send() transferred 0 bytes");
186     }
187 }
188 
189 void fsutil::send(const std::string &fileName, int to) {
190     int fd = ::open(fileName.c_str(), O_RDONLY);
191     size_t bytes = fsutil::size(fileName);
192     send(fd, bytes, to);
193     ::close(fd);
194 }
195 
196 
197 size_t fsutil::size(const std::string &fileName) {
198     struct stat st;
199     stat(fileName.c_str(), &st);
200     return st.st_size;
201 }
202 
203 bool fsutil::isDir(const std::string &dirName) {
204     struct stat buffer;
205     return (stat(dirName.c_str(), &buffer) == 0 && S_ISDIR(buffer.st_mode));
206 }
207 
208 bool fsutil::removeFile(const std::string &dirName) {
209     struct stat buffer;
210     if (stat(dirName.c_str(), &buffer) == 0 && S_ISREG(buffer.st_mode)) {
211         std::cerr << "removing file '" + dirName << "'" << std::endl;
212         return (::unlink(dirName.c_str()) == 0);
213     }
214     return false;
215 }
216 
217 bool fsutil::isFile(const std::string &fileName) {
218     struct stat buffer;
219     return (stat(fileName.c_str(), &buffer) == 0 && S_ISREG(buffer.st_mode));
220 }
221 
222 bool fsutil::isFileOrLink(const std::string &fileName) {
223     struct stat buffer;
224     return (stat(fileName.c_str(), &buffer) == 0 && (S_ISREG(buffer.st_mode) || (S_ISLNK(buffer.st_mode))));
225 }
226 
227 bool fsutil::isFile(const std::string &dirName, const std::string &fileName) {
228     std::string path = dirName + "/" + fileName;
229     return isFile(path);
230 }
231 
232 bool fsutil::isFileOrLink(const std::string &dirName, const std::string &fileName) {
233     std::string path = dirName + "/" + fileName;
234     return isFileOrLink(path);
235 }
236 
237 bool fsutil::hasFileSuffix(const std::string &fileName, const std::string &suffix) {
238     return strutil::endsWith(fileName, suffix);
239 }
240 
241 std::string fsutil::getFileNameEndingWith(const std::string &dir, const std::string &suffix) {
242     std::vector<std::string> matches;
243     visit(dir, false, [&](auto dir, auto n) { if (!dir && hasFileSuffix(n, suffix)) matches.push_back(n); });
244     if (matches.size() == 0) {
245         std::cout << "no file: *" << suffix << std::endl;
246     } else if (matches.size() > 1) {
247         std::cout << "many : *" << suffix << std::endl;
248     } else {
249         return *matches.begin();
250     }
251     return "";
252 }
253 
254 void fsutil::mkdir_p(char *path) {
255     char *sep = std::strrchr(path, '/');
256     if (sep != NULL) {
257         *sep = 0;
258         mkdir_p(path);
259         *sep = '/';
260     }
261     if (mkdir(path, 0777) && errno != EEXIST) {
262         printf("error while trying to create '%s'\n%m\n", path);
263     }
264 }
265 
266 std::string fsutil::getFile(const std::string &path) {
267     std::stringstream buf;
268     std::ifstream input(path.c_str());
269     buf << input.rdbuf();
270     return buf.str();
271 }
272 
273 BufferCursor *fsutil::getFileBufferCursor(const std::string &path) {
274     size_t s = size(path);
275     // read directly into buffer!  buffer(path.c_str());
276     char *buf = (char *) malloc(s + 1);
277     BufferCursor *buffer = new BufferCursor(buf, s + 1);
278     int fd = open(path.c_str(), O_RDONLY);
279     ::read(fd, buffer->getStart(), buffer->getSize());
280     close(fd);
281     return buffer;
282 }
283 
284 void fsutil::putFile(const std::string &path, const std::string &content) {
285     std::ofstream out(path);
286     out << content;
287     out.close();
288 }
289 
290 void fsutil::putFileBufferCursor(const std::string &path, BufferCursor *buffer) {
291     std::cerr << "who the hell called putFileBUffer" << std::endl;
292     ::exit(1);
293 }