1 /*
2     SPDX-FileCopyrightText: 1998-2007 Sebastian Trueg <trueg@k3b.org>
3     SPDX-License-Identifier: GPL-2.0-or-later
4 */
5 
6 #include "k3bfilecompilationsizehandler.h"
7 #include "k3bfileitem.h"
8 
9 #include <QDebug>
10 #include <QFile>
11 #include <QMap>
12 #include <QList>
13 
14 
15 // TODO: remove the items from the project if the savedSize differs
16 // with some info-widget: "Files xxx have changed on disk. Removing them from the project."
17 // or we just update the sizes!
18 
19 
usedBlocks(const KIO::filesize_t & bytes)20 static long usedBlocks( const KIO::filesize_t& bytes )
21 {
22     if( bytes % 2048 )
23         return bytes/2048 + 1;
24     else
25         return bytes/2048;
26 }
27 
28 
29 class InodeInfo
30 {
31 public:
InodeInfo()32     InodeInfo() {
33         number = 0;
34         savedSize = 0;
35     }
36 
37     /**
38      * How often has the file with
39      * the corresponding inode been added
40      */
41     int number;
42 
43     /**
44      * The size of the first added file. This has to be saved
45      * to check further addings and to avoid the following situation:
46      * A file with inode 1 is added, then deleted. Another file is created
47      * at inode 1 and added to the project. Now the first file gets
48      * removed and then the second. If we had not saved the size we would
49      * have added the size of the first and removed the size of the second
50      * file resulting in a corrupted project size.
51      * This way we always use the size of the first added file and may
52      * warn the user if sizes differ.
53      */
54     KIO::filesize_t savedSize;
55 
completeSize() const56     KIO::filesize_t completeSize() const { return savedSize*number; }
57 
58     /**
59      * In an iso9660 filesystem a file occupies complete blocks of 2048 bytes.
60      */
blocks() const61     K3b::Msf blocks() const { return K3b::Msf( usedBlocks(savedSize) ); }
62 
63     QList<K3b::DataItem*> items;
64 };
65 
66 
67 class K3b::FileCompilationSizeHandler::Private
68 {
69 public:
Private()70     Private()
71         : size(0) {
72     }
73 
clear()74     void clear() {
75         inodeMap.clear();
76         size = 0;
77         blocks = 0;
78     }
79 
addFile(K3b::FileItem * item,bool followSymlinks)80     void addFile( K3b::FileItem* item, bool followSymlinks ) {
81         InodeInfo& inodeInfo = inodeMap[item->localId(followSymlinks)];
82 
83         inodeInfo.items.append( item );
84 
85         if( inodeInfo.number == 0 ) {
86             inodeInfo.savedSize = item->itemSize( followSymlinks );
87 
88             size += inodeInfo.savedSize;
89             blocks += inodeInfo.blocks();
90         }
91 
92         inodeInfo.number++;
93     }
94 
addSpecialItem(K3b::DataItem * item)95     void addSpecialItem( K3b::DataItem* item ) {
96         // special files do not have a corresponding local file
97         // so we just add their k3bSize
98         size += item->size();
99         blocks += usedBlocks(item->size());
100         specialItems.append( item );
101     }
102 
removeFile(K3b::FileItem * item,bool followSymlinks)103     void removeFile( K3b::FileItem* item, bool followSymlinks ) {
104         InodeInfo& inodeInfo = inodeMap[item->localId(followSymlinks)];
105 
106         if( !inodeInfo.items.contains( item ) ) {
107             qCritical() << "(K3b::FileCompilationSizeHandler) "
108                      << item->localPath()
109                      << " has been removed without being added!" << endl;
110         }
111         else {
112             if( item->itemSize(followSymlinks) != inodeInfo.savedSize ) {
113                 qCritical() << "(K3b::FileCompilationSizeHandler) savedSize differs!" << endl;
114             }
115 
116             inodeInfo.items.removeOne( item );
117             inodeInfo.number--;
118             if( inodeInfo.number == 0 ) {
119                 size -= inodeInfo.savedSize;
120                 blocks -= inodeInfo.blocks();
121             }
122         }
123     }
124 
removeSpecialItem(K3b::DataItem * item)125     void removeSpecialItem( K3b::DataItem* item ) {
126         // special files do not have a corresponding local file
127         // so we just subtract their k3bSize
128         if( !specialItems.contains( item ) ) {
129             qCritical() << "(K3b::FileCompilationSizeHandler) Special item "
130                      << item->k3bName()
131                      << " has been removed without being added!" << endl;
132         }
133         else {
134             specialItems.removeOne( item );
135             size -= item->size();
136             blocks -= usedBlocks(item->size());
137         }
138     }
139 
140 
141     /**
142      * This maps from inodes to the number of occurrences of the inode.
143      */
144     QMap<K3b::FileItem::Id, InodeInfo> inodeMap;
145 
146     KIO::filesize_t size;
147     K3b::Msf blocks;
148 
149     QList<K3b::DataItem*> specialItems;
150 };
151 
152 
153 
FileCompilationSizeHandler()154 K3b::FileCompilationSizeHandler::FileCompilationSizeHandler()
155 {
156     d_symlinks = new Private;
157     d_noSymlinks = new Private;
158 }
159 
~FileCompilationSizeHandler()160 K3b::FileCompilationSizeHandler::~FileCompilationSizeHandler()
161 {
162     delete d_symlinks;
163     delete d_noSymlinks;
164 }
165 
166 
size(bool followSymlinks) const167 const KIO::filesize_t& K3b::FileCompilationSizeHandler::size( bool followSymlinks ) const
168 {
169     if( followSymlinks )
170         return d_noSymlinks->size;
171     else
172         return d_symlinks->size;
173 }
174 
175 
blocks(bool followSymlinks) const176 const K3b::Msf& K3b::FileCompilationSizeHandler::blocks( bool followSymlinks ) const
177 {
178     if( followSymlinks )
179         return d_noSymlinks->blocks;
180     else
181         return d_symlinks->blocks;
182 }
183 
184 
addFile(K3b::DataItem * item)185 void K3b::FileCompilationSizeHandler::addFile( K3b::DataItem* item )
186 {
187     if( item->isSpecialFile() ) {
188         d_symlinks->addSpecialItem( item );
189         d_noSymlinks->addSpecialItem( item );
190     }
191     else if( item->isFile() ) {
192         K3b::FileItem* fileItem = static_cast<K3b::FileItem*>( item );
193         d_symlinks->addFile( fileItem, false );
194         d_noSymlinks->addFile( fileItem, true );
195     }
196 }
197 
198 
removeFile(K3b::DataItem * item)199 void K3b::FileCompilationSizeHandler::removeFile( K3b::DataItem* item )
200 {
201     if( item->isSpecialFile() ) {
202         d_symlinks->removeSpecialItem( item );
203         d_noSymlinks->removeSpecialItem( item );
204     }
205     else if( item->isFile() ) {
206         K3b::FileItem* fileItem = static_cast<K3b::FileItem*>( item );
207         d_symlinks->removeFile( fileItem, false );
208         d_noSymlinks->removeFile( fileItem, true );
209     }
210 }
211 
212 
clear()213 void K3b::FileCompilationSizeHandler::clear()
214 {
215     d_symlinks->clear();
216     d_noSymlinks->clear();
217 }
218