GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/fs/virtfs/fsdir.cpp Lines: 209 260 80.4 %
Date: 2017-11-29 Branches: 136 282 48.2 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2013-2017  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
2
    std::string mWriteDir;
55
2
    std::string mBaseDir;
56
2
    std::string mUserDir;
57
    bool mPermitLinks = false;
58
    FsFuncs funcs;
59
}  // namespace
60
61
namespace FsDir
62
{
63
7376
    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
14752
            filename;
69

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

4348
        return new File(&funcs, fd);
80
    }
81
82
7376
    File *openRead(FsEntry *restrict const entry,
83
                   std::string filename)
84
    {
85
7376
        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
238
    void deinit()
101
    {
102
238
    }
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
240
    void init(const std::string &restrict name)
115
    {
116
720
        mBaseDir = getRealPath(getFileDir(name));
117
#endif  // defined(__native_client__)
118
119
240
        prepareFsPath(mBaseDir);
120
480
        mUserDir = getHomePath();
121
240
        prepareFsPath(mUserDir);
122
240
        initFuncs(&funcs);
123
240
    }
124
125
    void initFuncs(FsFuncs *restrict const ptr)
126
    {
127
240
        ptr->close = &FsDir::close;
128
240
        ptr->read = &FsDir::read;
129
240
        ptr->write = &FsDir::write;
130
240
        ptr->fileLength = &FsDir::fileLength;
131
240
        ptr->tell = &FsDir::tell;
132
240
        ptr->seek = &FsDir::seek;
133
240
        ptr->eof = &FsDir::eof;
134
240
        ptr->exists = &FsDir::exists;
135
240
        ptr->getRealDir = &FsDir::getRealDir;
136
240
        ptr->enumerate = &FsDir::enumerate;
137
240
        ptr->isDirectory = &FsDir::isDirectory;
138
240
        ptr->openRead = &FsDir::openRead;
139
240
        ptr->openWrite = &FsDir::openWrite;
140
240
        ptr->openAppend = &FsDir::openAppend;
141
240
        ptr->loadFile = &FsDir::loadFile;
142
240
        ptr->getFiles = &FsDir::getFiles;
143
240
        ptr->getFilesWithDir = &FsDir::getFilesWithDir;
144
240
        ptr->getDirs = &FsDir::getDirs;
145
240
        ptr->rwops_seek = &FsDir::rwops_seek;
146
240
        ptr->rwops_read = &FsDir::rwops_read;
147
240
        ptr->rwops_write = &FsDir::rwops_write;
148
240
        ptr->rwops_close = &FsDir::rwops_close;
149
#ifdef USE_SDL2
150
240
        ptr->rwops_size = &FsDir::rwops_size;
151
#endif  // USE_SDL2
152
    }
153
154
932
    FsFuncs *getFuncs()
155
    {
156
932
        return &funcs;
157
    }
158
159
540
    const char *getBaseDir()
160
    {
161
540
        return mBaseDir.c_str();
162
    }
163
164
1074
    const char *getUserDir()
165
    {
166
1074
        return mUserDir.c_str();
167
    }
168
169
138
    bool getRealDir(FsEntry *restrict const entry,
170
                    std::string filename,
171
                    std::string dirName A_UNUSED,
172
                    std::string &realDir)
173
    {
174
138
        const DirEntry *const dirEntry = static_cast<const DirEntry*>(entry);
175

276
        if (Files::existsLocal(dirEntry->rootSubDir + filename))
176
        {
177
136
            realDir = dirEntry->userDir;
178
68
            return true;
179
        }
180
        return false;
181
    }
182
183
15880
    bool exists(FsEntry *restrict const entry,
184
                std::string fileName,
185
                std::string dirName A_UNUSED)
186
    {
187
31760
        return Files::existsLocal(static_cast<DirEntry*>(entry)->rootSubDir +
188
31760
            fileName);
189
    }
190
191
46
    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
92
            dirName;
197
46
        const struct dirent *next_file = nullptr;
198
46
        DIR *const dir = opendir(path.c_str());
199
46
        if (dir != nullptr)
200
        {
201

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

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

1254
                    if (lstat(path.c_str(), &statbuf) == 0 &&
211
418
                        S_ISLNK(statbuf.st_mode) != 0)
212
                    {
213
                        continue;
214
                    }
215
                }
216
#endif  // WIN32
217
218
530
                bool found(false);
219
8448
                FOR_EACH (StringVectCIter, itn, names)
220
                {
221
5808
                    if (*itn == file)
222
                    {
223
                        found = true;
224
                        break;
225
                    }
226
                }
227
530
                if (found == false)
228
520
                    names.push_back(file);
229
            }
230
34
            closedir(dir);
231
        }
232
46
    }
233
234
1600
    bool isDirectory(FsEntry *restrict const entry,
235
                     std::string dirName,
236
                     bool &isDirFlag)
237
    {
238
3200
        std::string path = static_cast<DirEntry*>(entry)->rootSubDir + dirName;
239
240
        struct stat statbuf;
241
3200
        if (stat(path.c_str(), &statbuf) == 0)
242
        {
243
836
            isDirFlag = (S_ISDIR(statbuf.st_mode) != 0);
244
836
            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
40
    void permitLinks(const bool val)
307
    {
308
40
        mPermitLinks = val;
309
40
    }
310
311
36
    int close(File *restrict const file)
312
    {
313
36
        if (file == nullptr)
314
            return 0;
315
36
        delete file;
316
36
        return 1;
317
    }
318
319
12
    int64_t read(File *restrict const file,
320
                 void *restrict const buffer,
321
                 const uint32_t objSize,
322
                 const uint32_t objCount)
323
    {
324
12
        if (file == nullptr)
325
            return 0;
326
12
        FILEHTYPE fd = file->mFd;
327
12
        if (fd == FILEHDEFAULT)
328
        {
329
            reportAlways("FsDir::read file not opened.");
330
            return 0;
331
        }
332
#ifdef USE_FILE_FOPEN
333
24
        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
30
    int64_t fileLength(File *restrict const file)
368
    {
369
30
        if (file == nullptr)
370
            return -1;
371
30
        FILEHTYPE fd = file->mFd;
372
30
        if (fd == FILEHDEFAULT)
373
        {
374
            reportAlways("FsDir::fileLength file not opened.");
375
            return 0;
376
        }
377
#ifdef USE_FILE_FOPEN
378
30
        const long pos = ftell(fd);
379
30
        if (pos < 0)
380
        {
381
            reportAlways("FsDir::fileLength ftell error.");
382
            return -1;
383
        }
384
30
        fseek(fd, 0, SEEK_END);
385
30
        const long sz = ftell(fd);
386
30
        fseek(fd, pos, SEEK_SET);
387
30
        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
12
    int64_t tell(File *restrict const file)
400
    {
401
12
        if (file == nullptr)
402
            return -1;
403
404
12
        FILEHTYPE fd = file->mFd;
405
12
        if (fd == FILEHDEFAULT)
406
        {
407
            reportAlways("FsDir::tell file not opened.");
408
            return 0;
409
        }
410
#ifdef USE_FILE_FOPEN
411
12
        return ftell(fd);
412
#else  // USE_FILE_FOPEN
413
        return lseek(fd, 0, SEEK_CUR);
414
#endif  // USE_FILE_FOPEN
415
    }
416
417
6
    int seek(File *restrict const file,
418
             const uint64_t pos)
419
    {
420
6
        if (file == nullptr)
421
            return 0;
422
423
6
        FILEHTYPE fd = file->mFd;
424
6
        if (fd == FILEHDEFAULT)
425
        {
426
            reportAlways("FsDir::seek file not opened.");
427
            return 0;
428
        }
429
6
        const int64_t res = FILESEEK(fd, pos, SEEK_SET);
430
6
        if (res == -1)
431
            return 0;
432
6
        return 1;
433
    }
434
435
18
    int eof(File *restrict const file)
436
    {
437
18
        if (file == nullptr)
438
            return -1;
439
440
18
        FILEHTYPE fd = file->mFd;
441
18
        if (fd == FILEHDEFAULT)
442
        {
443
            reportAlways("FsDir::eof file not opened.");
444
            return 0;
445
        }
446
#ifdef USE_FILE_FOPEN
447
18
        const int flag = feof(fd);
448
18
        if (flag != 0)
449
            return 1;
450
18
        const int64_t pos = ftell(fd);
451
18
        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

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

7872
        if (Files::existsLocal(path) == false)
472
            return nullptr;
473
4450
        FILEHTYPE fd = FILEOPEN(path.c_str(),
474
            FILEOPEN_FLAG_READ);
475
4450
        if (fd == FILEHDEFAULT)
476
        {
477
            reportAlways("VirtFs::loadFile file open error: %s",
478
                filename.c_str());
479
            return nullptr;
480
        }
481
482
13350
        logger->log("Loaded %s/%s",
483
            dirEntry->userDir.c_str(),
484
            filename.c_str());
485
486
#ifdef USE_FILE_FOPEN
487
4450
        fseek(fd, 0, SEEK_END);
488
4450
        const long sz = ftell(fd);
489
4450
        if (sz < 0)
490
        {
491
            reportAlways("FsDir::fileLength ftell error.");
492
            if (fd != FILEHDEFAULT)
493
                FILECLOSE(fd);
494
            return nullptr;
495
        }
496
4450
        fseek(fd, 0, SEEK_SET);
497
4450
        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
4450
        char *restrict const buffer = new char[CAST_SIZE(fileSize)];
512
4450
        if (fileSize > 0)
513
4450
            buffer[fileSize - 1] = 0;
514
515
#ifdef USE_FILE_FOPEN
516
8900
        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
4450
        if (cnt <= 0)
522
        {
523
            delete [] buffer;
524
            if (fd != FILEHDEFAULT)
525
                FILECLOSE(fd);
526
            return nullptr;
527
        }
528
529
        if (fd != FILEHDEFAULT)
530
4450
            FILECLOSE(fd);
531
532
        return buffer;
533
    }
534
535
26
    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
52
            dirName;
541
26
        const struct dirent *next_file = nullptr;
542
26
        DIR *const dir = opendir(path.c_str());
543
26
        if (dir != nullptr)
544
        {
545

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

1218
                if (file == "." || file == "..")
550
128
                    continue;
551
#ifndef WIN32
552
576
                if (mPermitLinks == false)
553
                {
554

1008
                    if (lstat(path.c_str(), &statbuf) == 0 &&
555
336
                        S_ISLNK(statbuf.st_mode) != 0)
556
                    {
557
                        continue;
558
                    }
559
                }
560
#endif  // WIN32
561
562
1688
                const std::string filePath = pathJoin(path, file);
563
1152
                if (stat(filePath.c_str(), &statbuf) == 0)
564
                {
565
576
                    if (S_ISDIR(statbuf.st_mode) != 0)
566
                        continue;
567
                }
568
569
536
                bool found(false);
570
9074
                FOR_EACH (StringVectCIter, itn, names)
571
                {
572
6394
                    if (*itn == file)
573
                    {
574
                        found = true;
575
                        break;
576
                    }
577
                }
578
536
                if (found == false)
579
536
                    names.push_back(file);
580
            }
581
22
            closedir(dir);
582
        }
583
26
    }
584
585
342
    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
684
            dirName;
591
342
        const struct dirent *next_file = nullptr;
592
342
        DIR *const dir = opendir(path.c_str());
593
342
        if (dir != nullptr)
594
        {
595

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

494
                if (file == "." || file == "..")
600
88
                    continue;
601
#ifndef WIN32
602
220
                if (mPermitLinks == false)
603
                {
604

660
                    if (lstat(path.c_str(), &statbuf) == 0 &&
605
220
                        S_ISLNK(statbuf.st_mode) != 0)
606
                    {
607
                        continue;
608
                    }
609
                }
610
#endif  // WIN32
611
612
644
                const std::string filePath = pathJoin(path, file);
613
440
                if (stat(filePath.c_str(), &statbuf) == 0)
614
                {
615
220
                    if (S_ISDIR(statbuf.st_mode) != 0)
616
                        continue;
617
                }
618
619
204
                bool found(false);
620
2550
                FOR_EACH (StringVectCIter, itn, names)
621
                {
622
1530
                    if (*itn == file)
623
                    {
624
                        found = true;
625
                        break;
626
                    }
627
                }
628
204
                if (found == false)
629
816
                    names.push_back(pathJoin(dirName, file));
630
            }
631
18
            closedir(dir);
632
        }
633
342
    }
634
635
20
    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
40
            dirName;
641
20
        const struct dirent *next_file = nullptr;
642
20
        DIR *const dir = opendir(path.c_str());
643
20
        if (dir != nullptr)
644
        {
645

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

492
                if (file == "." || file == "..")
650
228
                    continue;
651
#ifndef WIN32
652
222
                if (mPermitLinks == false)
653
                {
654

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

6
}  // namespace VirtFs