GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/fs/virtfs/fsdir.cpp Lines: 209 257 81.3 %
Date: 2021-03-17 Branches: 157 310 50.6 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2013-2019  The ManaPlus Developers
4
 *  Copyright (C) 2019-2021  Andrei Karas
5
 *
6
 *  This file is part of The ManaPlus Client.
7
 *
8
 *  This program is free software; you can redistribute it and/or modify
9
 *  it under the terms of the GNU General Public License as published by
10
 *  the Free Software Foundation; either version 2 of the License, or
11
 *  any later version.
12
 *
13
 *  This program is distributed in the hope that it will be useful,
14
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 *  GNU General Public License for more details.
17
 *
18
 *  You should have received a copy of the GNU General Public License
19
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
 */
21
22
#include "fs/virtfs/fsdir.h"
23
24
#include "fs/files.h"
25
#include "fs/mkdir.h"
26
#include "fs/paths.h"
27
28
#include "fs/virtfs/direntry.h"
29
#include "fs/virtfs/file.h"
30
#include "fs/virtfs/fsdirrwops.h"
31
#include "fs/virtfs/fsfuncs.h"
32
#include "fs/virtfs/list.h"
33
34
#include "utils/cast.h"
35
#include "utils/checkutils.h"
36
#include "utils/foreach.h"
37
#include "utils/stdmove.h"
38
#include "utils/stringutils.h"
39
40
#include <dirent.h>
41
#include <unistd.h>
42
43
#include <sys/types.h>
44
#include <sys/stat.h>
45
46
#include "debug.h"
47
48
extern const char *dirSeparator;
49
50
namespace VirtFs
51
{
52
53
namespace
54
{
55
1
    std::string mWriteDir;
56
1
    std::string mBaseDir;
57
1
    std::string mUserDir;
58
    bool mPermitLinks = false;
59
    FsFuncs funcs;
60
}  // namespace
61
62
namespace FsDir
63
{
64
2680
    File *openInternal(FsEntry *restrict const entry,
65
                       const std::string &filename,
66
                       const FILEMTYPE mode)
67
    {
68
        const std::string path = static_cast<DirEntry*>(entry)->rootSubDir +
69
5360
            filename;
70

2680
        if (Files::existsLocal(path) == false)
71
            return nullptr;
72
1639
        FILEHTYPE fd = FILEOPEN(path.c_str(),
73
            mode);
74
1639
        if (fd == FILEHDEFAULT)
75
        {
76
            reportAlways("VirtFs::open file open error: %s",
77
                filename.c_str())
78
            return nullptr;
79
        }
80

1639
        return new File(&funcs, fd);
81
    }
82
83
2680
    File *openRead(FsEntry *restrict const entry,
84
                   std::string filename)
85
    {
86
2680
        return openInternal(entry, filename, FILEOPEN_FLAG_READ);
87
    }
88
89
    File *openWrite(FsEntry *restrict const entry,
90
                    const std::string &filename)
91
    {
92
        return openInternal(entry, filename, FILEOPEN_FLAG_WRITE);
93
    }
94
95
    File *openAppend(FsEntry *restrict const entry,
96
                     const std::string &filename)
97
    {
98
        return openInternal(entry, filename, FILEOPEN_FLAG_APPEND);
99
    }
100
101
119
    void deinit()
102
    {
103
119
    }
104
105
#if defined(__native_client__)
106
    void init(const std::string &restrict name A_UNUSED)
107
    {
108
        mBaseDir = "/";
109
#elif defined(ANDROID)
110
    void init(const std::string &restrict name A_UNUSED)
111
    {
112
        mBaseDir = getRealPath(".");
113
#else  // defined(__native_client__)
114
115
120
    void init(const std::string &restrict name)
116
    {
117
360
        mBaseDir = getRealPath(getFileDir(name));
118
#endif  // defined(__native_client__)
119
120
120
        prepareFsPath(mBaseDir);
121
240
        mUserDir = getHomePath();
122
120
        prepareFsPath(mUserDir);
123
120
        initFuncs(&funcs);
124
120
    }
125
126
    void initFuncs(FsFuncs *restrict const ptr)
127
    {
128
120
        ptr->close = &FsDir::close;
129
120
        ptr->read = &FsDir::read;
130
120
        ptr->write = &FsDir::write;
131
120
        ptr->fileLength = &FsDir::fileLength;
132
120
        ptr->tell = &FsDir::tell;
133
120
        ptr->seek = &FsDir::seek;
134
120
        ptr->eof = &FsDir::eof;
135
120
        ptr->exists = &FsDir::exists;
136
120
        ptr->getRealDir = &FsDir::getRealDir;
137
120
        ptr->enumerate = &FsDir::enumerate;
138
120
        ptr->isDirectory = &FsDir::isDirectory;
139
120
        ptr->openRead = &FsDir::openRead;
140
120
        ptr->openWrite = &FsDir::openWrite;
141
120
        ptr->openAppend = &FsDir::openAppend;
142
120
        ptr->loadFile = &FsDir::loadFile;
143
120
        ptr->getFiles = &FsDir::getFiles;
144
120
        ptr->getFilesWithDir = &FsDir::getFilesWithDir;
145
120
        ptr->getDirs = &FsDir::getDirs;
146
120
        ptr->rwops_seek = &FsDir::rwops_seek;
147
120
        ptr->rwops_read = &FsDir::rwops_read;
148
120
        ptr->rwops_write = &FsDir::rwops_write;
149
120
        ptr->rwops_close = &FsDir::rwops_close;
150
#ifdef USE_SDL2
151
        ptr->rwops_size = &FsDir::rwops_size;
152
#endif  // USE_SDL2
153
    }
154
155
521
    FsFuncs *getFuncs()
156
    {
157
521
        return &funcs;
158
    }
159
160
304
    const char *getBaseDir()
161
    {
162
304
        return mBaseDir.c_str();
163
    }
164
165
605
    const char *getUserDir()
166
    {
167
605
        return mUserDir.c_str();
168
    }
169
170
1439
    bool getRealDir(FsEntry *restrict const entry,
171
                    std::string filename,
172
                    std::string dirName A_UNUSED,
173
                    std::string &realDir)
174
    {
175
1439
        const DirEntry *const dirEntry = static_cast<const DirEntry*>(entry);
176

2878
        if (Files::existsLocal(dirEntry->rootSubDir + filename))
177
        {
178
1598
            realDir = dirEntry->userDir;
179
799
            return true;
180
        }
181
        return false;
182
    }
183
184
8499
    bool exists(FsEntry *restrict const entry,
185
                std::string fileName,
186
                std::string dirName A_UNUSED)
187
    {
188
16998
        return Files::existsLocal(static_cast<DirEntry*>(entry)->rootSubDir +
189
16998
            fileName);
190
    }
191
192
23
    void enumerate(FsEntry *restrict const entry,
193
                   std::string dirName,
194
                   StringVect &names)
195
    {
196
        const std::string path = static_cast<DirEntry*>(entry)->rootSubDir +
197
46
            dirName;
198
23
        const dirent *next_file = nullptr;
199
23
        DIR *const dir = opendir(path.c_str());
200
23
        if (dir != nullptr)
201
        {
202

328
            while ((next_file = readdir(dir)) != nullptr)
203
            {
204
1210
                const std::string file = next_file->d_name;
205

605
                if (file == "." || file == "..")
206
34
                    continue;
207
#ifndef WIN32
208
277
                if (mPermitLinks == false)
209
                {
210
                    struct stat statbuf;
211

651
                    if (lstat(path.c_str(), &statbuf) == 0 &&
212
217
                        S_ISLNK(statbuf.st_mode) != 0)
213
                    {
214
                        continue;
215
                    }
216
                }
217
#endif  // WIN32
218
219
277
                bool found(false);
220
4625
                FOR_EACH (StringVectCIter, itn, names)
221
                {
222
3245
                    if (*itn == file)
223
                    {
224
                        found = true;
225
                        break;
226
                    }
227
                }
228
277
                if (found == false)
229
272
                    names.push_back(file);
230
            }
231
17
            closedir(dir);
232
        }
233
23
    }
234
235
910
    bool isDirectory(FsEntry *restrict const entry,
236
                     std::string dirName,
237
                     bool &isDirFlag)
238
    {
239
1820
        std::string path = static_cast<DirEntry*>(entry)->rootSubDir + dirName;
240
241
        struct stat statbuf;
242
1820
        if (stat(path.c_str(), &statbuf) == 0)
243
        {
244
486
            isDirFlag = (S_ISDIR(statbuf.st_mode) != 0);
245
486
            return true;
246
        }
247
        return false;
248
    }
249
250
    bool isSymbolicLink(std::string name)
251
    {
252
        prepareFsPath(name);
253
        if (checkPath(name) == false)
254
        {
255
            reportAlways("FsDir::isSymbolicLink invalid path: %s",
256
                name.c_str())
257
            return false;
258
        }
259
#ifndef WIN32
260
        if (mPermitLinks == false)
261
            return false;
262
263
        struct stat statbuf;
264
        return lstat(name.c_str(), &statbuf) == 0 &&
265
            S_ISLNK(statbuf.st_mode) != 0;
266
#else
267
        return false;
268
#endif  // WIN32
269
    }
270
271
    void freeList(List *restrict const handle)
272
    {
273
        delete handle;
274
    }
275
276
    bool setWriteDir(std::string newDir)
277
    {
278
        prepareFsPath(newDir);
279
        mWriteDir = STD_MOVE(newDir);
280
        if (findLast(mWriteDir, std::string(dirSeparator)) == false)
281
            mWriteDir += dirSeparator;
282
        return true;
283
    }
284
285
    bool mkdir(std::string dirname)
286
    {
287
        prepareFsPath(dirname);
288
        if (mWriteDir.empty())
289
        {
290
            reportAlways("FsDir::mkdir write dir is empty")
291
            return false;
292
        }
293
        return mkdir_r((mWriteDir + dirname).c_str()) != -1;
294
    }
295
296
    bool remove(std::string filename)
297
    {
298
        prepareFsPath(filename);
299
        if (mWriteDir.empty())
300
        {
301
            reportAlways("FsDir::remove write dir is empty")
302
            return false;
303
        }
304
        return ::remove((mWriteDir + filename).c_str()) != 0;
305
    }
306
307
20
    void permitLinks(const bool val)
308
    {
309
20
        mPermitLinks = val;
310
20
    }
311
312
18
    int close(File *restrict const file)
313
    {
314
18
        if (file == nullptr)
315
            return 0;
316
18
        delete file;
317
18
        return 1;
318
    }
319
320
6
    int64_t read(File *restrict const file,
321
                 void *restrict const buffer,
322
                 const uint32_t objSize,
323
                 const uint32_t objCount)
324
    {
325
6
        if (file == nullptr)
326
            return 0;
327
6
        FILEHTYPE fd = file->mFd;
328
6
        if (fd == FILEHDEFAULT)
329
        {
330
            reportAlways("FsDir::read file not opened.")
331
            return 0;
332
        }
333
#ifdef USE_FILE_FOPEN
334
12
        return fread(buffer, objSize, objCount, fd);
335
#else  // USE_FILE_FOPEN
336
        int max = objSize * objCount;
337
        int cnt = ::read(fd, buffer, max);
338
        if (cnt <= 0)
339
            return cnt;
340
        return cnt / objSize;
341
#endif  // USE_FILE_FOPEN
342
    }
343
344
    int64_t write(File *restrict const file,
345
                  const void *restrict const buffer,
346
                  const uint32_t objSize,
347
                  const uint32_t objCount)
348
    {
349
        if (file == nullptr)
350
            return 0;
351
        FILEHTYPE fd = file->mFd;
352
        if (fd == FILEHDEFAULT)
353
        {
354
            reportAlways("FsDir::write file not opened.")
355
            return 0;
356
        }
357
#ifdef USE_FILE_FOPEN
358
        return fwrite(buffer, objSize, objCount, fd);
359
#else  // USE_FILE_FOPEN
360
        int max = objSize * objCount;
361
        int cnt = ::write(fd, buffer, max);
362
        if (cnt <= 0)
363
            return cnt;
364
        return cnt / objSize;
365
#endif  // USE_FILE_FOPEN
366
    }
367
368
15
    int64_t fileLength(File *restrict const file)
369
    {
370
15
        if (file == nullptr)
371
            return -1;
372
15
        FILEHTYPE fd = file->mFd;
373
15
        if (fd == FILEHDEFAULT)
374
        {
375
            reportAlways("FsDir::fileLength file not opened.")
376
            return 0;
377
        }
378
#ifdef USE_FILE_FOPEN
379
15
        const long pos = ftell(fd);
380
15
        if (pos < 0)
381
        {
382
            reportAlways("FsDir::fileLength ftell error.")
383
            return -1;
384
        }
385
15
        fseek(fd, 0, SEEK_END);
386
15
        const long sz = ftell(fd);
387
15
        fseek(fd, pos, SEEK_SET);
388
15
        return sz;
389
#else  // USE_FILE_FOPEN
390
        struct stat statbuf;
391
        if (fstat(fd, &statbuf) == -1)
392
        {
393
            reportAlways("FsDir::fileLength error.")
394
            return -1;
395
        }
396
        return static_cast<int64_t>(statbuf.st_size);
397
#endif  // USE_FILE_FOPEN
398
    }
399
400
6
    int64_t tell(File *restrict const file)
401
    {
402
6
        if (file == nullptr)
403
            return -1;
404
405
6
        FILEHTYPE fd = file->mFd;
406
6
        if (fd == FILEHDEFAULT)
407
        {
408
            reportAlways("FsDir::tell file not opened.")
409
            return 0;
410
        }
411
#ifdef USE_FILE_FOPEN
412
6
        return ftell(fd);
413
#else  // USE_FILE_FOPEN
414
        return lseek(fd, 0, SEEK_CUR);
415
#endif  // USE_FILE_FOPEN
416
    }
417
418
3
    int seek(File *restrict const file,
419
             const uint64_t pos)
420
    {
421
3
        if (file == nullptr)
422
            return 0;
423
424
3
        FILEHTYPE fd = file->mFd;
425
3
        if (fd == FILEHDEFAULT)
426
        {
427
            reportAlways("FsDir::seek file not opened.")
428
            return 0;
429
        }
430
3
        const int64_t res = FILESEEK(fd, pos, SEEK_SET);
431
3
        if (res == -1)
432
            return 0;
433
3
        return 1;
434
    }
435
436
9
    int eof(File *restrict const file)
437
    {
438
9
        if (file == nullptr)
439
            return -1;
440
441
9
        FILEHTYPE fd = file->mFd;
442
9
        if (fd == FILEHDEFAULT)
443
        {
444
            reportAlways("FsDir::eof file not opened.")
445
            return 0;
446
        }
447
#ifdef USE_FILE_FOPEN
448
9
        const int flag = feof(fd);
449
9
        if (flag != 0)
450
            return 1;
451
9
        const int64_t pos = ftell(fd);
452
9
        const int64_t len = fileLength(file);
453
#else  // USE_FILE_FOPEN
454
        const int64_t pos = lseek(fd, 0, SEEK_CUR);
455
        struct stat statbuf;
456
        if (fstat(fd, &statbuf) == -1)
457
        {
458
            reportAlways("FsDir::fileLength error.")
459
            return -1;
460
        }
461
        const int64_t len = static_cast<int64_t>(statbuf.st_size);
462
#endif  // USE_FILE_FOPEN
463

9
        return static_cast<int>(pos < 0 || len < 0 || pos >= len);
464
    }
465
466
4373
    const char *loadFile(FsEntry *restrict const entry,
467
                         std::string filename,
468
                         int &restrict fileSize)
469
    {
470
4373
        const DirEntry *const dirEntry = static_cast<DirEntry*>(entry);
471
8746
        const std::string path = dirEntry->rootSubDir + filename;
472

4373
        if (Files::existsLocal(path) == false)
473
            return nullptr;
474
2499
        FILEHTYPE fd = FILEOPEN(path.c_str(),
475
            FILEOPEN_FLAG_READ);
476
2499
        if (fd == FILEHDEFAULT)
477
        {
478
            reportAlways("VirtFs::loadFile file open error: %s",
479
                filename.c_str())
480
            return nullptr;
481
        }
482
483
4998
        logger->log("Loaded %s/%s",
484
            dirEntry->userDir.c_str(),
485
2499
            filename.c_str());
486
487
#ifdef USE_FILE_FOPEN
488
2499
        fseek(fd, 0, SEEK_END);
489
2499
        const long sz = ftell(fd);
490
2499
        if (sz < 0)
491
        {
492
            reportAlways("FsDir::fileLength ftell error.")
493
            if (fd != FILEHDEFAULT)
494
                FILECLOSE(fd);
495
            return nullptr;
496
        }
497
2499
        fseek(fd, 0, SEEK_SET);
498
2499
        fileSize = static_cast<int>(sz);
499
#else  // USE_FILE_FOPEN
500
        struct stat statbuf;
501
        if (fstat(fd, &statbuf) == -1)
502
        {
503
            reportAlways("FsDir::fileLength error.")
504
            if (fd != FILEHDEFAULT)
505
                FILECLOSE(fd);
506
            return nullptr;
507
        }
508
        fileSize = static_cast<int>(statbuf.st_size);
509
#endif  // USE_FILE_FOPEN
510
511
        // Allocate memory and load the file
512
2499
        char *restrict const buffer = new char[CAST_SIZE(fileSize)];
513
2499
        if (fileSize > 0)
514
2499
            buffer[fileSize - 1] = 0;
515
516
#ifdef USE_FILE_FOPEN
517
4998
        const int cnt = CAST_S32(fread(buffer, 1, fileSize, fd));
518
#else  // USE_FILE_FOPEN
519
        const int cnt = ::read(fd, buffer, fileSize);
520
#endif  // USE_FILE_FOPEN
521
522
2499
        if (cnt <= 0)
523
        {
524
            delete [] buffer;
525
            if (fd != FILEHDEFAULT)
526
                FILECLOSE(fd);
527
            return nullptr;
528
        }
529
530
        if (fd != FILEHDEFAULT)
531
2499
            FILECLOSE(fd);
532
533
        return buffer;
534
    }
535
536
13
    void getFiles(FsEntry *restrict const entry,
537
                  std::string dirName,
538
                  StringVect &names)
539
    {
540
        const std::string path = static_cast<DirEntry*>(entry)->rootSubDir +
541
26
            dirName;
542
13
        const dirent *next_file = nullptr;
543
13
        DIR *const dir = opendir(path.c_str());
544
13
        if (dir != nullptr)
545
        {
546

339
            while ((next_file = readdir(dir)) != nullptr)
547
            {
548
                struct stat statbuf;
549
1261
                const std::string file = next_file->d_name;
550

645
                if (file == "." || file == "..")
551
51
                    continue;
552
#ifndef WIN32
553
306
                if (mPermitLinks == false)
554
                {
555

540
                    if (lstat(path.c_str(), &statbuf) == 0 &&
556
180
                        S_ISLNK(statbuf.st_mode) != 0)
557
                    {
558
                        continue;
559
                    }
560
                }
561
#endif  // WIN32
562
563
889
                const std::string filePath = pathJoin(path, file);
564
612
                if (stat(filePath.c_str(), &statbuf) == 0)
565
                {
566

306
                    if (S_ISDIR(statbuf.st_mode) != 0)
567
                        continue;
568
                }
569
570
277
                bool found(false);
571
4816
                FOR_EACH (StringVectCIter, itn, names)
572
                {
573
3431
                    if (*itn == file)
574
                    {
575
                        found = true;
576
                        break;
577
                    }
578
                }
579
277
                if (found == false)
580
277
                    names.push_back(file);
581
            }
582
11
            closedir(dir);
583
        }
584
13
    }
585
586
173
    void getFilesWithDir(FsEntry *restrict const entry,
587
                         const std::string &dirName,
588
                         StringVect &names)
589
    {
590
        const std::string path = static_cast<DirEntry*>(entry)->rootSubDir +
591
346
            dirName;
592
173
        const dirent *next_file = nullptr;
593
173
        DIR *const dir = opendir(path.c_str());
594
173
        if (dir != nullptr)
595
        {
596

137
            while ((next_file = readdir(dir)) != nullptr)
597
            {
598
                struct stat statbuf;
599
485
                const std::string file = next_file->d_name;
600

247
                if (file == "." || file == "..")
601
27
                    continue;
602
#ifndef WIN32
603
110
                if (mPermitLinks == false)
604
                {
605

330
                    if (lstat(path.c_str(), &statbuf) == 0 &&
606
110
                        S_ISLNK(statbuf.st_mode) != 0)
607
                    {
608
                        continue;
609
                    }
610
                }
611
#endif  // WIN32
612
613
321
                const std::string filePath = pathJoin(path, file);
614
220
                if (stat(filePath.c_str(), &statbuf) == 0)
615
                {
616

110
                    if (S_ISDIR(statbuf.st_mode) != 0)
617
                        continue;
618
                }
619
620
101
                bool found(false);
621
1266
                FOR_EACH (StringVectCIter, itn, names)
622
                {
623
761
                    if (*itn == file)
624
                    {
625
                        found = true;
626
                        break;
627
                    }
628
                }
629
101
                if (found == false)
630
404
                    names.push_back(pathJoin(dirName, file));
631
            }
632
9
            closedir(dir);
633
        }
634
173
    }
635
636
10
    void getDirs(FsEntry *restrict const entry,
637
                 std::string dirName,
638
                 StringVect &names)
639
    {
640
        const std::string path = static_cast<DirEntry*>(entry)->rootSubDir +
641
20
            dirName;
642
10
        const dirent *next_file = nullptr;
643
10
        DIR *const dir = opendir(path.c_str());
644
10
        if (dir != nullptr)
645
        {
646

139
            while ((next_file = readdir(dir)) != nullptr)
647
            {
648
                struct stat statbuf;
649
424
                const std::string file = next_file->d_name;
650

254
                if (file == "." || file == "..")
651
100
                    continue;
652
#ifndef WIN32
653
115
                if (mPermitLinks == false)
654
                {
655

345
                    if (lstat(path.c_str(), &statbuf) == 0 &&
656
115
                        S_ISLNK(statbuf.st_mode) != 0)
657
                    {
658
                        continue;
659
                    }
660
                }
661
#endif  // WIN32
662
663
261
                const std::string filePath = pathJoin(path, file);
664
230
                if (stat(filePath.c_str(), &statbuf) == 0)
665
                {
666
115
                    if (S_ISDIR(statbuf.st_mode) == 0)
667
                        continue;
668
                }
669
670
31
                bool found(false);
671
294
                FOR_EACH (StringVectCIter, itn, names)
672
                {
673
139
                    if (*itn == file)
674
                    {
675
                        found = true;
676
                        break;
677
                    }
678
                }
679
31
                if (found == false)
680
31
                    names.push_back(file);
681
            }
682
8
            closedir(dir);
683
        }
684
10
    }
685
}  // namespace FsDir
686
687

3
}  // namespace VirtFs