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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

251
                if (file == "." || file == "..")
600
27
                    continue;
601
#ifndef WIN32
602
112
                if (mPermitLinks == false)
603
                {
604

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

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

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

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

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

3
}  // namespace VirtFs