1 /**
2  * @file node.cpp
3  * @brief Classes for accessing local and remote nodes
4  *
5  * (c) 2013-2014 by Mega Limited, Auckland, New Zealand
6  *
7  * This file is part of the MEGA SDK - Client Access Engine.
8  *
9  * Applications using the MEGA API must present a valid application key
10  * and comply with the the rules set forth in the Terms of Service.
11  *
12  * The MEGA SDK 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.
15  *
16  * @copyright Simplified (2-clause) BSD License.
17  *
18  * You should have received a copy of the license along with this
19  * program.
20  */
21 
22 #include "mega/node.h"
23 #include "mega/megaclient.h"
24 #include "mega/megaapp.h"
25 #include "mega/share.h"
26 #include "mega/serialize64.h"
27 #include "mega/base64.h"
28 #include "mega/sync.h"
29 #include "mega/transfer.h"
30 #include "mega/transferslot.h"
31 #include "mega/logging.h"
32 
33 namespace mega {
34 
Node(MegaClient * cclient,node_vector * dp,handle h,handle ph,nodetype_t t,m_off_t s,handle u,const char * fa,m_time_t ts)35 Node::Node(MegaClient* cclient, node_vector* dp, handle h, handle ph,
36            nodetype_t t, m_off_t s, handle u, const char* fa, m_time_t ts)
37 {
38     client = cclient;
39     outshares = NULL;
40     pendingshares = NULL;
41     tag = 0;
42     appdata = NULL;
43 
44     nodehandle = h;
45     parenthandle = ph;
46 
47     parent = NULL;
48 
49 #ifdef ENABLE_SYNC
50     localnode = NULL;
51     syncget = NULL;
52 
53     syncdeleted = SYNCDEL_NONE;
54     todebris_it = client->todebris.end();
55     tounlink_it = client->tounlink.end();
56 #endif
57 
58     type = t;
59 
60     size = s;
61     owner = u;
62 
63     copystring(&fileattrstring, fa);
64 
65     ctime = ts;
66 
67     inshare = NULL;
68     sharekey = NULL;
69     foreignkey = false;
70 
71     plink = NULL;
72 
73     memset(&changed, 0, sizeof changed);
74 
75     Node* p;
76 
77     client->nodes[h] = this;
78 
79     // folder link access: first returned record defines root node and
80     // identity
81     if (ISUNDEF(*client->rootnodes))
82     {
83         *client->rootnodes = h;
84     }
85 
86     if (t >= ROOTNODE && t <= RUBBISHNODE)
87     {
88         client->rootnodes[t - ROOTNODE] = h;
89     }
90 
91     // set parent linkage or queue for delayed parent linkage in case of
92     // out-of-order delivery
93     if ((p = client->nodebyhandle(ph)))
94     {
95         setparent(p);
96     }
97     else
98     {
99         dp->push_back(this);
100     }
101 
102     client->mFingerprints.newnode(this);
103 }
104 
~Node()105 Node::~Node()
106 {
107     if (keyApplied())
108     {
109         client->mAppliedKeyNodeCount--;
110         assert(client->mAppliedKeyNodeCount >= 0);
111     }
112 
113     // abort pending direct reads
114     client->preadabort(this);
115 
116     // remove node's fingerprint from hash
117     if (!client->mOptimizePurgeNodes)
118     {
119         client->mFingerprints.remove(this);
120     }
121 
122 #ifdef ENABLE_SYNC
123     // remove from todebris node_set
124     if (todebris_it != client->todebris.end())
125     {
126         client->todebris.erase(todebris_it);
127     }
128 
129     // remove from tounlink node_set
130     if (tounlink_it != client->tounlink.end())
131     {
132         client->tounlink.erase(tounlink_it);
133     }
134 #endif
135 
136     if (outshares)
137     {
138         // delete outshares, including pointers from users for this node
139         for (share_map::iterator it = outshares->begin(); it != outshares->end(); it++)
140         {
141             delete it->second;
142         }
143         delete outshares;
144     }
145 
146     if (pendingshares)
147     {
148         // delete pending shares
149         for (share_map::iterator it = pendingshares->begin(); it != pendingshares->end(); it++)
150         {
151             delete it->second;
152         }
153         delete pendingshares;
154     }
155 
156 
157     if (!client->mOptimizePurgeNodes)
158     {
159         // remove from parent's children
160         if (parent)
161         {
162             parent->children.erase(child_it);
163         }
164 
165         Node* fa = firstancestor();
166         handle ancestor = fa->nodehandle;
167         if (ancestor == client->rootnodes[0] || ancestor == client->rootnodes[1] || ancestor == client->rootnodes[2] || fa->inshare)
168         {
169             client->mNodeCounters[firstancestor()->nodehandle] -= subnodeCounts();
170         }
171 
172         if (inshare)
173         {
174             client->mNodeCounters.erase(nodehandle);
175         }
176 
177         // delete child-parent associations (normally not used, as nodes are
178         // deleted bottom-up)
179         for (node_list::iterator it = children.begin(); it != children.end(); it++)
180         {
181             (*it)->parent = NULL;
182         }
183     }
184 
185     if (plink)
186     {
187         client->mPublicLinks.erase(nodehandle);
188     }
189 
190     delete plink;
191     delete inshare;
192     delete sharekey;
193 
194 #ifdef ENABLE_SYNC
195     // sync: remove reference from local filesystem node
196     if (localnode)
197     {
198         localnode->deleted = true;
199         localnode->node = NULL;
200     }
201 
202     // in case this node is currently being transferred for syncing: abort transfer
203     delete syncget;
204 #endif
205 }
206 
setkeyfromjson(const char * k)207 void Node::setkeyfromjson(const char* k)
208 {
209     if (keyApplied()) --client->mAppliedKeyNodeCount;
210     Node::copystring(&nodekeydata, k);
211     if (keyApplied()) ++client->mAppliedKeyNodeCount;
212     assert(client->mAppliedKeyNodeCount >= 0);
213 }
214 
215 // update node key and decrypt attributes
setkey(const byte * newkey)216 void Node::setkey(const byte* newkey)
217 {
218     if (newkey)
219     {
220         if (keyApplied()) --client->mAppliedKeyNodeCount;
221         nodekeydata.assign(reinterpret_cast<const char*>(newkey), (type == FILENODE) ? FILENODEKEYLENGTH : FOLDERNODEKEYLENGTH);
222         if (keyApplied()) ++client->mAppliedKeyNodeCount;
223         assert(client->mAppliedKeyNodeCount >= 0);
224     }
225 
226     setattr();
227 }
228 
229 // parse serialized node and return Node object - updates nodes hash and parent
230 // mismatch vector
unserialize(MegaClient * client,const string * d,node_vector * dp)231 Node* Node::unserialize(MegaClient* client, const string* d, node_vector* dp)
232 {
233     handle h, ph;
234     nodetype_t t;
235     m_off_t s;
236     handle u;
237     const byte* k = NULL;
238     const char* fa;
239     m_time_t ts;
240     const byte* skey;
241     const char* ptr = d->data();
242     const char* end = ptr + d->size();
243     unsigned short ll;
244     Node* n;
245     int i;
246     char isExported = '\0';
247     char hasLinkCreationTs = '\0';
248 
249     if (ptr + sizeof s + 2 * MegaClient::NODEHANDLE + MegaClient::USERHANDLE + 2 * sizeof ts + sizeof ll > end)
250     {
251         return NULL;
252     }
253 
254     s = MemAccess::get<m_off_t>(ptr);
255     ptr += sizeof s;
256 
257     if (s < 0 && s >= -RUBBISHNODE)
258     {
259         t = (nodetype_t)-s;
260     }
261     else
262     {
263         t = FILENODE;
264     }
265 
266     h = 0;
267     memcpy((char*)&h, ptr, MegaClient::NODEHANDLE);
268     ptr += MegaClient::NODEHANDLE;
269 
270     ph = 0;
271     memcpy((char*)&ph, ptr, MegaClient::NODEHANDLE);
272     ptr += MegaClient::NODEHANDLE;
273 
274     if (!ph)
275     {
276         ph = UNDEF;
277     }
278 
279     u = 0;
280     memcpy((char*)&u, ptr, MegaClient::USERHANDLE);
281     ptr += MegaClient::USERHANDLE;
282 
283     // FIME: use m_time_t / Serialize64 instead
284     ptr += sizeof(time_t);
285 
286     ts = (uint32_t)MemAccess::get<time_t>(ptr);
287     ptr += sizeof(time_t);
288 
289     if ((t == FILENODE) || (t == FOLDERNODE))
290     {
291         int keylen = ((t == FILENODE) ? FILENODEKEYLENGTH : FOLDERNODEKEYLENGTH);
292 
293         if (ptr + keylen + 8 + sizeof(short) > end)
294         {
295             return NULL;
296         }
297 
298         k = (const byte*)ptr;
299         ptr += keylen;
300     }
301 
302     if (t == FILENODE)
303     {
304         ll = MemAccess::get<unsigned short>(ptr);
305         ptr += sizeof ll;
306 
307         if (ptr + ll > end)
308         {
309             return NULL;
310         }
311 
312         fa = ptr;
313         ptr += ll;
314     }
315     else
316     {
317         fa = NULL;
318     }
319 
320     if (ptr + sizeof isExported + sizeof hasLinkCreationTs > end)
321     {
322         return NULL;
323     }
324 
325     isExported = MemAccess::get<char>(ptr);
326     ptr += sizeof(isExported);
327 
328     hasLinkCreationTs = MemAccess::get<char>(ptr);
329     ptr += sizeof(hasLinkCreationTs);
330 
331     for (i = 6; i--;)
332     {
333         if (ptr + (unsigned char)*ptr < end)
334         {
335             ptr += (unsigned char)*ptr + 1;
336         }
337     }
338 
339     if (ptr + sizeof(short) > end)
340     {
341         return NULL;
342     }
343 
344     short numshares = MemAccess::get<short>(ptr);
345     ptr += sizeof(numshares);
346 
347     if (numshares)
348     {
349         if (ptr + SymmCipher::KEYLENGTH > end)
350         {
351             return NULL;
352         }
353 
354         skey = (const byte*)ptr;
355         ptr += SymmCipher::KEYLENGTH;
356     }
357     else
358     {
359         skey = NULL;
360     }
361 
362     n = new Node(client, dp, h, ph, t, s, u, fa, ts);
363 
364     if (k)
365     {
366         n->setkey(k);
367     }
368 
369     // read inshare, outshares, or pending shares
370     while (numshares)   // inshares: -1, outshare/s: num_shares
371     {
372         int direction = (numshares > 0) ? -1 : 0;
373         NewShare *newShare = Share::unserialize(direction, h, skey, &ptr, end);
374         if (!newShare)
375         {
376             LOG_err << "Failed to unserialize Share";
377             break;
378         }
379 
380         client->newshares.push_back(newShare);
381         if (numshares > 0)  // outshare/s
382         {
383             numshares--;
384         }
385         else    // inshare
386         {
387             break;
388         }
389     }
390 
391     ptr = n->attrs.unserialize(ptr, end);
392     if (!ptr)
393     {
394         delete n;
395         return NULL;
396     }
397 
398     // It's needed to re-normalize node names because
399     // the updated version of utf8proc doesn't provide
400     // exactly the same output as the previous one that
401     // we were using
402     attr_map::iterator it = n->attrs.map.find('n');
403     if (it != n->attrs.map.end())
404     {
405         client->fsaccess->normalize(&(it->second));
406     }
407 
408     PublicLink *plink = NULL;
409     if (isExported)
410     {
411         if (ptr + MegaClient::NODEHANDLE + sizeof(m_time_t) + sizeof(bool) > end)
412         {
413             delete n;
414             return NULL;
415         }
416 
417         handle ph = 0;
418         memcpy((char*)&ph, ptr, MegaClient::NODEHANDLE);
419         ptr += MegaClient::NODEHANDLE;
420         m_time_t ets = MemAccess::get<m_time_t>(ptr);
421         ptr += sizeof(ets);
422         bool takendown = MemAccess::get<bool>(ptr);
423         ptr += sizeof(takendown);
424 
425         m_time_t cts = 0;
426         if (hasLinkCreationTs)
427         {
428             cts = MemAccess::get<m_time_t>(ptr);
429             ptr += sizeof(cts);
430         }
431 
432         plink = new PublicLink(ph, cts, ets, takendown);
433         client->mPublicLinks[n->nodehandle] = plink->ph;
434     }
435     n->plink = plink;
436 
437     n->setfingerprint();
438 
439     if (ptr == end)
440     {
441         return n;
442     }
443     else
444     {
445         delete n;
446         return NULL;
447     }
448 }
449 
450 // serialize node - nodes with pending or RSA keys are unsupported
serialize(string * d)451 bool Node::serialize(string* d)
452 {
453     // do not serialize encrypted nodes
454     if (attrstring)
455     {
456         LOG_warn << "Trying to serialize an encrypted node";
457 
458         //Last attempt to decrypt the node
459         applykey();
460         setattr();
461 
462         if (attrstring)
463         {
464             LOG_warn << "Skipping undecryptable node";
465             return false;
466         }
467     }
468 
469     switch (type)
470     {
471         case FILENODE:
472             if ((int)nodekeydata.size() != FILENODEKEYLENGTH)
473             {
474                 return false;
475             }
476             break;
477 
478         case FOLDERNODE:
479             if ((int)nodekeydata.size() != FOLDERNODEKEYLENGTH)
480             {
481                 return false;
482             }
483             break;
484 
485         default:
486             if (nodekeydata.size())
487             {
488                 return false;
489             }
490     }
491 
492     unsigned short ll;
493     short numshares;
494     m_off_t s;
495 
496     s = type ? -type : size;
497 
498     d->append((char*)&s, sizeof s);
499 
500     d->append((char*)&nodehandle, MegaClient::NODEHANDLE);
501 
502     if (parent)
503     {
504         d->append((char*)&parent->nodehandle, MegaClient::NODEHANDLE);
505     }
506     else
507     {
508         d->append("\0\0\0\0\0", MegaClient::NODEHANDLE);
509     }
510 
511     d->append((char*)&owner, MegaClient::USERHANDLE);
512 
513     // FIXME: use Serialize64
514     time_t ts = 0;  // we don't want to break backward compatibiltiy by changing the size (where m_time_t differs)
515     d->append((char*)&ts, sizeof(ts));
516 
517     ts = (time_t)ctime;
518     d->append((char*)&ts, sizeof(ts));
519 
520     d->append(nodekeydata);
521 
522     if (type == FILENODE)
523     {
524         ll = static_cast<unsigned short>(fileattrstring.size() + 1);
525         d->append((char*)&ll, sizeof ll);
526         d->append(fileattrstring.c_str(), ll);
527     }
528 
529     char isExported = plink ? 1 : 0;
530     d->append((char*)&isExported, 1);
531 
532     char hasLinkCreationTs = plink ? 1 : 0;
533     d->append((char*)&hasLinkCreationTs, 1);
534 
535     d->append("\0\0\0\0\0", 6); // Use these bytes for extensions
536 
537     if (inshare)
538     {
539         numshares = -1;
540     }
541     else
542     {
543         numshares = 0;
544         if (outshares)
545         {
546             numshares += (short)outshares->size();
547         }
548         if (pendingshares)
549         {
550             numshares += (short)pendingshares->size();
551         }
552     }
553 
554     d->append((char*)&numshares, sizeof numshares);
555 
556     if (numshares)
557     {
558         d->append((char*)sharekey->key, SymmCipher::KEYLENGTH);
559 
560         if (inshare)
561         {
562             inshare->serialize(d);
563         }
564         else
565         {
566             if (outshares)
567             {
568                 for (share_map::iterator it = outshares->begin(); it != outshares->end(); it++)
569                 {
570                     it->second->serialize(d);
571                 }
572             }
573             if (pendingshares)
574             {
575                 for (share_map::iterator it = pendingshares->begin(); it != pendingshares->end(); it++)
576                 {
577                     it->second->serialize(d);
578                 }
579             }
580         }
581     }
582 
583     attrs.serialize(d);
584 
585     if (isExported)
586     {
587         d->append((char*) &plink->ph, MegaClient::NODEHANDLE);
588         d->append((char*) &plink->ets, sizeof(plink->ets));
589         d->append((char*) &plink->takendown, sizeof(plink->takendown));
590         if (hasLinkCreationTs)
591         {
592             d->append((char*) &plink->cts, sizeof(plink->cts));
593         }
594     }
595 
596     return true;
597 }
598 
599 // copy remainder of quoted string (no unescaping, use for base64 data only)
copystring(string * s,const char * p)600 void Node::copystring(string* s, const char* p)
601 {
602     if (p)
603     {
604         const char* pp;
605 
606         if ((pp = strchr(p, '"')))
607         {
608             s->assign(p, pp - p);
609         }
610         else
611         {
612             *s = p;
613         }
614     }
615     else
616     {
617         s->clear();
618     }
619 }
620 
621 // decrypt attrstring and check magic number prefix
decryptattr(SymmCipher * key,const char * attrstring,size_t attrstrlen)622 byte* Node::decryptattr(SymmCipher* key, const char* attrstring, size_t attrstrlen)
623 {
624     if (attrstrlen)
625     {
626         int l = int(attrstrlen * 3 / 4 + 3);
627         byte* buf = new byte[l];
628 
629         l = Base64::atob(attrstring, buf, l);
630 
631         if (!(l & (SymmCipher::BLOCKSIZE - 1)))
632         {
633             key->cbc_decrypt(buf, l);
634 
635             if (!memcmp(buf, "MEGA{\"", 6))
636             {
637                 return buf;
638             }
639         }
640 
641         delete[] buf;
642     }
643 
644     return NULL;
645 }
646 
parseattr(byte * bufattr,AttrMap & attrs,m_off_t size,m_time_t & mtime,string & fileName,string & fingerprint,FileFingerprint & ffp)647 void Node::parseattr(byte *bufattr, AttrMap &attrs, m_off_t size, m_time_t &mtime , string &fileName, string &fingerprint, FileFingerprint &ffp)
648 {
649     JSON json;
650     nameid name;
651     string *t;
652 
653     json.begin((char*)bufattr + 5);
654     while ((name = json.getnameid()) != EOO && json.storeobject((t = &attrs.map[name])))
655     {
656         JSON::unescape(t);
657     }
658 
659     attr_map::iterator it = attrs.map.find('n');   // filename
660     if (it == attrs.map.end())
661     {
662         fileName = "CRYPTO_ERROR";
663     }
664     else if (it->second.empty())
665     {
666         fileName = "BLANK";
667     }
668 
669     it = attrs.map.find('c');   // checksum
670     if (it != attrs.map.end())
671     {
672         if (ffp.unserializefingerprint(&it->second))
673         {
674             ffp.size = size;
675             mtime = ffp.mtime;
676 
677             char bsize[sizeof(size) + 1];
678             int l = Serialize64::serialize((byte *)bsize, size);
679             char *buf = new char[l * 4 / 3 + 4];
680             char ssize = static_cast<char>('A' + Base64::btoa((const byte *)bsize, l, buf));
681 
682             string result(1, ssize);
683             result.append(buf);
684             result.append(it->second);
685             delete [] buf;
686 
687             fingerprint = result;
688         }
689     }
690 }
691 
692 // return temporary SymmCipher for this nodekey
nodecipher()693 SymmCipher* Node::nodecipher()
694 {
695     if (client->tmpnodecipher.setkey(&nodekeydata))
696     {
697         return &client->tmpnodecipher;
698     }
699 
700     return NULL;
701 }
702 
703 // decrypt attributes and build attribute hash
setattr()704 void Node::setattr()
705 {
706     byte* buf;
707     SymmCipher* cipher;
708 
709     if (attrstring && (cipher = nodecipher()) && (buf = decryptattr(cipher, attrstring->c_str(), attrstring->size())))
710     {
711         JSON json;
712         nameid name;
713         string* t;
714 
715         attrs.map.clear();
716         json.begin((char*)buf + 5);
717 
718         while ((name = json.getnameid()) != EOO && json.storeobject((t = &attrs.map[name])))
719         {
720             JSON::unescape(t);
721 
722             if (name == 'n')
723             {
724                 client->fsaccess->normalize(t);
725             }
726         }
727 
728         setfingerprint();
729 
730         delete[] buf;
731 
732         attrstring.reset();
733     }
734 }
735 
736 // if present, configure FileFingerprint from attributes
737 // otherwise, the file's fingerprint is derived from the file's mtime/size/key
setfingerprint()738 void Node::setfingerprint()
739 {
740     if (type == FILENODE && nodekeydata.size() >= sizeof crc)
741     {
742         client->mFingerprints.remove(this);
743 
744         attr_map::iterator it = attrs.map.find('c');
745 
746         if (it != attrs.map.end())
747         {
748             if (!unserializefingerprint(&it->second))
749             {
750                 LOG_warn << "Invalid fingerprint";
751             }
752         }
753 
754         // if we lack a valid FileFingerprint for this file, use file's key,
755         // size and client timestamp instead
756         if (!isvalid)
757         {
758             memcpy(crc.data(), nodekeydata.data(), sizeof crc);
759             mtime = ctime;
760         }
761 
762         client->mFingerprints.add(this);
763     }
764 }
765 
766 // return file/folder name or special status strings
displayname() const767 const char* Node::displayname() const
768 {
769     // not yet decrypted
770     if (attrstring)
771     {
772         LOG_debug << "NO_KEY " << type << " " << size << " " << Base64Str<MegaClient::NODEHANDLE>(nodehandle);
773 #ifdef ENABLE_SYNC
774         if (localnode)
775         {
776             LOG_debug << "Local name: " << localnode->name;
777         }
778 #endif
779         return "NO_KEY";
780     }
781 
782     attr_map::const_iterator it;
783 
784     it = attrs.map.find('n');
785 
786     if (it == attrs.map.end())
787     {
788         if (type < ROOTNODE || type > RUBBISHNODE)
789         {
790             LOG_debug << "CRYPTO_ERROR " << type << " " << size << " " << nodehandle;
791 #ifdef ENABLE_SYNC
792             if (localnode)
793             {
794                 LOG_debug << "Local name: " << localnode->name;
795             }
796 #endif
797         }
798         return "CRYPTO_ERROR";
799     }
800 
801     if (!it->second.size())
802     {
803         LOG_debug << "BLANK " << type << " " << size << " " << nodehandle;
804 #ifdef ENABLE_SYNC
805         if (localnode)
806         {
807             LOG_debug << "Local name: " << localnode->name;
808         }
809 #endif
810         return "BLANK";
811     }
812 
813     return it->second.c_str();
814 }
815 
displaypath() const816 string Node::displaypath() const
817 {
818     // factored from nearly identical functions in megapi_impl and megacli
819     string path;
820     const Node* n = this;
821     for (; n; n = n->parent)
822     {
823         switch (n->type)
824         {
825         case FOLDERNODE:
826             path.insert(0, n->displayname());
827 
828             if (n->inshare)
829             {
830                 path.insert(0, ":");
831                 if (n->inshare->user)
832                 {
833                     path.insert(0, n->inshare->user->email);
834                 }
835                 else
836                 {
837                     path.insert(0, "UNKNOWN");
838                 }
839                 return path;
840             }
841             break;
842 
843         case INCOMINGNODE:
844             path.insert(0, "//in");
845             return path;
846 
847         case ROOTNODE:
848             return path.empty() ? "/" : path;
849 
850         case RUBBISHNODE:
851             path.insert(0, "//bin");
852             return path;
853 
854         case TYPE_UNKNOWN:
855         case FILENODE:
856             path.insert(0, n->displayname());
857         }
858         path.insert(0, "/");
859     }
860     return path;
861 }
862 
863 // returns position of file attribute or 0 if not present
hasfileattribute(fatype t) const864 int Node::hasfileattribute(fatype t) const
865 {
866     return Node::hasfileattribute(&fileattrstring, t);
867 }
868 
hasfileattribute(const string * fileattrstring,fatype t)869 int Node::hasfileattribute(const string *fileattrstring, fatype t)
870 {
871     char buf[24];
872 
873     sprintf(buf, ":%u*", t);
874     return static_cast<int>(fileattrstring->find(buf) + 1);
875 }
876 
877 // attempt to apply node key - sets nodekey to a raw key if successful
applykey()878 bool Node::applykey()
879 {
880     if (type > FOLDERNODE)
881     {
882         //Root nodes contain an empty attrstring
883         attrstring.reset();
884     }
885 
886     if (keyApplied() || !nodekeydata.size())
887     {
888         return false;
889     }
890 
891     int l = -1;
892     size_t t = 0;
893     handle h;
894     const char* k = NULL;
895     SymmCipher* sc = &client->key;
896     handle me = client->loggedin() ? client->me : *client->rootnodes;
897 
898     while ((t = nodekeydata.find_first_of(':', t)) != string::npos)
899     {
900         // compound key: locate suitable subkey (always symmetric)
901         h = 0;
902 
903         l = Base64::atob(nodekeydata.c_str() + (nodekeydata.find_last_of('/', t) + 1), (byte*)&h, sizeof h);
904         t++;
905 
906         if (l == MegaClient::USERHANDLE)
907         {
908             // this is a user handle - reject if it's not me
909             if (h != me)
910             {
911                 continue;
912             }
913         }
914         else
915         {
916             // look for share key if not folder access with folder master key
917             if (h != me)
918             {
919                 Node* n;
920 
921                 // this is a share node handle - check if we have node and the
922                 // share key
923                 if (!(n = client->nodebyhandle(h)) || !n->sharekey)
924                 {
925                     continue;
926                 }
927 
928                 sc = n->sharekey;
929 
930                 // this key will be rewritten when the node leaves the outbound share
931                 foreignkey = true;
932             }
933         }
934 
935         k = nodekeydata.c_str() + t;
936         break;
937     }
938 
939     // no: found => personal key, use directly
940     // otherwise, no suitable key available yet - bail (it might arrive soon)
941     if (!k)
942     {
943         if (l < 0)
944         {
945             k = nodekeydata.c_str();
946         }
947         else
948         {
949             return false;
950         }
951     }
952 
953     byte key[FILENODEKEYLENGTH];
954     unsigned keylength = (type == FILENODE) ? FILENODEKEYLENGTH : FOLDERNODEKEYLENGTH;
955 
956     if (client->decryptkey(k, key, keylength, sc, 0, nodehandle))
957     {
958         client->mAppliedKeyNodeCount++;
959         nodekeydata.assign((const char*)key, keylength);
960         setattr();
961     }
962 
963     assert(keyApplied());
964     return true;
965 }
966 
subnodeCounts() const967 NodeCounter Node::subnodeCounts() const
968 {
969     NodeCounter nc;
970     for (Node *child : children)
971     {
972         nc += child->subnodeCounts();
973     }
974     if (type == FILENODE)
975     {
976         nc.files += 1;
977         nc.storage += size;
978         if (parent && parent->type == FILENODE)
979         {
980             nc.versions += 1;
981             nc.versionStorage += size;
982         }
983     }
984     else if (type == FOLDERNODE)
985     {
986         nc.folders += 1;
987     }
988     return nc;
989 }
990 
991 // returns whether node was moved
setparent(Node * p)992 bool Node::setparent(Node* p)
993 {
994     if (p == parent)
995     {
996         return false;
997     }
998 
999     NodeCounter nc;
1000     bool gotnc = false;
1001 
1002     Node *originalancestor = firstancestor();
1003     handle oah = originalancestor->nodehandle;
1004     if (oah == client->rootnodes[0] || oah == client->rootnodes[1] || oah == client->rootnodes[2] || originalancestor->inshare)
1005     {
1006         nc = subnodeCounts();
1007         gotnc = true;
1008 
1009         // nodes moving from cloud drive to rubbish for example, or between inshares from the same user.
1010         client->mNodeCounters[oah] -= nc;
1011     }
1012 
1013     if (parent)
1014     {
1015         parent->children.erase(child_it);
1016     }
1017 
1018 #ifdef ENABLE_SYNC
1019     Node *oldparent = parent;
1020 #endif
1021 
1022     parent = p;
1023 
1024     if (parent)
1025     {
1026         child_it = parent->children.insert(parent->children.end(), this);
1027     }
1028 
1029     Node* newancestor = firstancestor();
1030     handle nah = newancestor->nodehandle;
1031     if (nah == client->rootnodes[0] || nah == client->rootnodes[1] || nah == client->rootnodes[2] || newancestor->inshare)
1032     {
1033         if (!gotnc)
1034         {
1035             nc = subnodeCounts();
1036         }
1037 
1038         client->mNodeCounters[nah] += nc;
1039     }
1040 
1041 #ifdef ENABLE_SYNC
1042     // if we are moving an entire sync, don't cancel GET transfers
1043     if (!localnode || localnode->parent)
1044     {
1045         // if the new location is not synced, cancel all GET transfers
1046         while (p)
1047         {
1048             if (p->localnode)
1049             {
1050                 break;
1051             }
1052 
1053             p = p->parent;
1054         }
1055 
1056         if (!p || p->type == FILENODE)
1057         {
1058             TreeProcDelSyncGet tdsg;
1059             client->proctree(this, &tdsg);
1060         }
1061     }
1062 
1063     if (oldparent && oldparent->localnode)
1064     {
1065         oldparent->localnode->treestate(oldparent->localnode->checkstate());
1066     }
1067 #endif
1068 
1069     return true;
1070 }
1071 
firstancestor()1072 Node* Node::firstancestor()
1073 {
1074     Node* n = this;
1075     while (n->parent != NULL)
1076     {
1077         n = n->parent;
1078     }
1079     return n;
1080 }
1081 
1082 // returns 1 if n is under p, 0 otherwise
isbelow(Node * p) const1083 bool Node::isbelow(Node* p) const
1084 {
1085     const Node* n = this;
1086 
1087     for (;;)
1088     {
1089         if (!n)
1090         {
1091             return false;
1092         }
1093 
1094         if (n == p)
1095         {
1096             return true;
1097         }
1098 
1099         n = n->parent;
1100     }
1101 }
1102 
setpubliclink(handle ph,m_time_t cts,m_time_t ets,bool takendown)1103 void Node::setpubliclink(handle ph, m_time_t cts, m_time_t ets, bool takendown)
1104 {
1105     if (!plink) // creation
1106     {
1107         assert(client->mPublicLinks.find(nodehandle) == client->mPublicLinks.end());
1108         plink = new PublicLink(ph, cts, ets, takendown);
1109     }
1110     else            // update
1111     {
1112         assert(client->mPublicLinks.find(nodehandle) != client->mPublicLinks.end());
1113         plink->ph = ph;
1114         plink->cts = cts;
1115         plink->ets = ets;
1116         plink->takendown = takendown;
1117     }
1118     client->mPublicLinks[nodehandle] = ph;
1119 }
1120 
PublicLink(handle ph,m_time_t cts,m_time_t ets,bool takendown)1121 PublicLink::PublicLink(handle ph, m_time_t cts, m_time_t ets, bool takendown)
1122 {
1123     this->ph = ph;
1124     this->cts = cts;
1125     this->ets = ets;
1126     this->takendown = takendown;
1127 }
1128 
PublicLink(PublicLink * plink)1129 PublicLink::PublicLink(PublicLink *plink)
1130 {
1131     this->ph = plink->ph;
1132     this->cts = plink->cts;
1133     this->ets = plink->ets;
1134     this->takendown = plink->takendown;
1135 }
1136 
isExpired()1137 bool PublicLink::isExpired()
1138 {
1139     if (!ets)       // permanent link: ets=0
1140         return false;
1141 
1142     m_time_t t = m_time();
1143     return ets < t;
1144 }
1145 
1146 #ifdef ENABLE_SYNC
1147 // set, change or remove LocalNode's parent and name/localname/slocalname.
1148 // newlocalpath must be a full path and must not point to an empty string.
1149 // no shortname allowed as the last path component.
setnameparent(LocalNode * newparent,LocalPath * newlocalpath,std::unique_ptr<LocalPath> newshortname)1150 void LocalNode::setnameparent(LocalNode* newparent, LocalPath* newlocalpath, std::unique_ptr<LocalPath> newshortname)
1151 {
1152     if (!sync)
1153     {
1154         LOG_err << "LocalNode::init() was never called";
1155         assert(false);
1156         return;
1157     }
1158 
1159     bool newnode = localname.empty();
1160     Node* todelete = NULL;
1161     int nc = 0;
1162     Sync* oldsync = NULL;
1163 
1164     if (parent)
1165     {
1166         // remove existing child linkage
1167         parent->children.erase(&localname);
1168 
1169         if (slocalname)
1170         {
1171             parent->schildren.erase(slocalname.get());
1172             slocalname.reset();
1173         }
1174     }
1175 
1176     if (newlocalpath)
1177     {
1178         // extract name component from localpath, check for rename unless newnode
1179         size_t p = newlocalpath->getLeafnameByteIndex(*sync->client->fsaccess);
1180 
1181         // has the name changed?
1182         if (!newlocalpath->backEqual(p, localname))
1183         {
1184             // set new name
1185             localname = newlocalpath->subpathFrom(p);
1186             name = localname.toName(*sync->client->fsaccess);
1187 
1188             if (node)
1189             {
1190                 if (name != node->attrs.map['n'])
1191                 {
1192                     if (node->type == FILENODE)
1193                     {
1194                         treestate(TREESTATE_SYNCING);
1195                     }
1196                     else
1197                     {
1198                         sync->client->app->syncupdate_treestate(this);
1199                     }
1200 
1201                     string prevname = node->attrs.map['n'];
1202                     int creqtag = sync->client->reqtag;
1203 
1204                     // set new name
1205                     node->attrs.map['n'] = name;
1206                     sync->client->reqtag = sync->tag;
1207                     sync->client->setattr(node, prevname.c_str());
1208                     sync->client->reqtag = creqtag;
1209                 }
1210             }
1211         }
1212     }
1213 
1214     if (parent && parent != newparent && !sync->client->destructorRunning)
1215     {
1216         treestate(TREESTATE_NONE);
1217     }
1218 
1219     if (newparent)
1220     {
1221         if (newparent != parent)
1222         {
1223             parent = newparent;
1224 
1225             if (!newnode && node)
1226             {
1227                 assert(parent->node);
1228 
1229                 int creqtag = sync->client->reqtag;
1230                 sync->client->reqtag = sync->tag;
1231                 LOG_debug << "Moving node: " << node->displayname() << " to " << parent->node->displayname();
1232                 if (sync->client->rename(node, parent->node, SYNCDEL_NONE, node->parent ? node->parent->nodehandle : UNDEF) == API_EACCESS
1233                         && sync != parent->sync)
1234                 {
1235                     LOG_debug << "Rename not permitted. Using node copy/delete";
1236 
1237                     // save for deletion
1238                     todelete = node;
1239                 }
1240                 sync->client->reqtag = creqtag;
1241 
1242                 if (type == FILENODE)
1243                 {
1244                     ts = TREESTATE_SYNCING;
1245                 }
1246             }
1247 
1248             if (sync != parent->sync)
1249             {
1250                 LOG_debug << "Moving files between different syncs";
1251                 oldsync = sync;
1252             }
1253 
1254             if (todelete || oldsync)
1255             {
1256                 // prepare localnodes for a sync change or/and a copy operation
1257                 LocalTreeProcMove tp(parent->sync, todelete != NULL);
1258                 sync->client->proclocaltree(this, &tp);
1259                 nc = tp.nc;
1260             }
1261         }
1262 
1263         // (we don't construct a UTF-8 or sname for the root path)
1264         parent->children[&localname] = this;
1265 
1266         if (newshortname && *newshortname != localname)
1267         {
1268             slocalname = std::move(newshortname);
1269             parent->schildren[slocalname.get()] = this;
1270         }
1271         else
1272         {
1273             slocalname.reset();
1274         }
1275 
1276         treestate(TREESTATE_NONE);
1277 
1278         if (todelete)
1279         {
1280             // complete the copy/delete operation
1281             dstime nds = NEVER;
1282             sync->client->syncup(parent, &nds);
1283 
1284             // check if nodes can be immediately created
1285             bool immediatecreation = (int) sync->client->synccreate.size() == nc;
1286 
1287             sync->client->syncupdate();
1288 
1289             // try to keep nodes in syncdebris if they can't be immediately created
1290             // to avoid uploads
1291             sync->client->movetosyncdebris(todelete, immediatecreation || oldsync->inshare);
1292         }
1293 
1294         if (oldsync)
1295         {
1296             // update local cache if there is a sync change
1297             oldsync->cachenodes();
1298             sync->cachenodes();
1299         }
1300     }
1301 
1302     if (newlocalpath)
1303     {
1304         LocalTreeProcUpdateTransfers tput;
1305         sync->client->proclocaltree(this, &tput);
1306     }
1307 }
1308 
1309 // delay uploads by 1.1 s to prevent server flooding while a file is still being written
bumpnagleds()1310 void LocalNode::bumpnagleds()
1311 {
1312     if (!sync)
1313     {
1314         LOG_err << "LocalNode::init() was never called";
1315         assert(false);
1316         return;
1317     }
1318 
1319     nagleds = sync->client->waiter->ds + 11;
1320 }
1321 
LocalNode()1322 LocalNode::LocalNode()
1323 : deleted{false}
1324 , created{false}
1325 , reported{false}
1326 , checked{false}
1327 {}
1328 
1329 // initialize fresh LocalNode object - must be called exactly once
init(Sync * csync,nodetype_t ctype,LocalNode * cparent,LocalPath & cfullpath,std::unique_ptr<LocalPath> shortname)1330 void LocalNode::init(Sync* csync, nodetype_t ctype, LocalNode* cparent, LocalPath& cfullpath, std::unique_ptr<LocalPath> shortname)
1331 {
1332     sync = csync;
1333     parent = NULL;
1334     node = NULL;
1335     notseen = 0;
1336     deleted = false;
1337     created = false;
1338     reported = false;
1339     syncxfer = true;
1340     newnode.reset();
1341     parent_dbid = 0;
1342     slocalname = NULL;
1343 
1344     ts = TREESTATE_NONE;
1345     dts = TREESTATE_NONE;
1346 
1347     type = ctype;
1348     syncid = sync->client->nextsyncid();
1349 
1350     bumpnagleds();
1351 
1352     if (cparent)
1353     {
1354         setnameparent(cparent, &cfullpath, std::move(shortname));
1355     }
1356     else
1357     {
1358         localname = cfullpath;
1359         slocalname.reset(shortname && *shortname != localname ? shortname.release() : nullptr);
1360         name = localname.toPath(*sync->client->fsaccess);
1361     }
1362 
1363     scanseqno = sync->scanseqno;
1364 
1365     // mark fsid as not valid
1366     fsid_it = sync->client->fsidnode.end();
1367 
1368     // enable folder notification
1369     if (type == FOLDERNODE)
1370     {
1371         sync->dirnotify->addnotify(this, cfullpath.editStringDirect());
1372     }
1373 
1374     sync->client->syncactivity = true;
1375 
1376     sync->client->totalLocalNodes++;
1377     sync->localnodes[type]++;
1378 }
1379 
1380 // update treestates back to the root LocalNode, inform app about changes
treestate(treestate_t newts)1381 void LocalNode::treestate(treestate_t newts)
1382 {
1383     if (!sync)
1384     {
1385         LOG_err << "LocalNode::init() was never called";
1386         assert(false);
1387         return;
1388     }
1389 
1390     if (newts != TREESTATE_NONE)
1391     {
1392         ts = newts;
1393     }
1394 
1395     if (ts != dts)
1396     {
1397         sync->client->app->syncupdate_treestate(this);
1398     }
1399 
1400     if (parent && ((newts == TREESTATE_NONE && ts != TREESTATE_NONE)
1401                    || (ts != dts && (!(ts == TREESTATE_SYNCED && parent->ts == TREESTATE_SYNCED))
1402                                  && (!(ts == TREESTATE_SYNCING && parent->ts == TREESTATE_SYNCING))
1403                                  && (!(ts == TREESTATE_PENDING && (parent->ts == TREESTATE_PENDING
1404                                                                    || parent->ts == TREESTATE_SYNCING))))))
1405     {
1406         treestate_t state = TREESTATE_NONE;
1407         if (newts != TREESTATE_NONE && ts == TREESTATE_SYNCING)
1408         {
1409             state = TREESTATE_SYNCING;
1410         }
1411         else
1412         {
1413             state = parent->checkstate();
1414         }
1415 
1416         parent->treestate(state);
1417     }
1418 
1419     dts = ts;
1420 }
1421 
checkstate()1422 treestate_t LocalNode::checkstate()
1423 {
1424     if (type == FILENODE)
1425         return ts;
1426 
1427     treestate_t state = TREESTATE_SYNCED;
1428     for (localnode_map::iterator it = children.begin(); it != children.end(); it++)
1429     {
1430         if (it->second->ts == TREESTATE_SYNCING)
1431         {
1432             state = TREESTATE_SYNCING;
1433             break;
1434         }
1435 
1436         if (it->second->ts == TREESTATE_PENDING && ts == TREESTATE_SYNCED)
1437         {
1438             state = TREESTATE_PENDING;
1439         }
1440     }
1441     return state;
1442 }
1443 
setnode(Node * cnode)1444 void LocalNode::setnode(Node* cnode)
1445 {
1446     if (node && (node != cnode) && node->localnode)
1447     {
1448         node->localnode = NULL;
1449     }
1450 
1451     deleted = false;
1452 
1453     node = cnode;
1454 
1455     if (node)
1456     {
1457         node->localnode = this;
1458     }
1459 }
1460 
setnotseen(int newnotseen)1461 void LocalNode::setnotseen(int newnotseen)
1462 {
1463     if (!sync)
1464     {
1465         LOG_err << "LocalNode::init() was never called";
1466         assert(false);
1467         return;
1468     }
1469 
1470     if (!newnotseen)
1471     {
1472         if (notseen)
1473         {
1474             sync->client->localsyncnotseen.erase(notseen_it);
1475         }
1476 
1477         notseen = 0;
1478         scanseqno = sync->scanseqno;
1479     }
1480     else
1481     {
1482         if (!notseen)
1483         {
1484             notseen_it = sync->client->localsyncnotseen.insert(this).first;
1485         }
1486 
1487         notseen = newnotseen;
1488     }
1489 }
1490 
1491 // set fsid - assume that an existing assignment of the same fsid is no longer current and revoke
setfsid(handle newfsid,handlelocalnode_map & fsidnodes)1492 void LocalNode::setfsid(handle newfsid, handlelocalnode_map& fsidnodes)
1493 {
1494     if (!sync)
1495     {
1496         LOG_err << "LocalNode::init() was never called";
1497         assert(false);
1498         return;
1499     }
1500 
1501     if (fsid_it != fsidnodes.end())
1502     {
1503         if (newfsid == fsid)
1504         {
1505             return;
1506         }
1507 
1508         fsidnodes.erase(fsid_it);
1509     }
1510 
1511     fsid = newfsid;
1512 
1513     pair<handlelocalnode_map::iterator, bool> r = fsidnodes.insert(std::make_pair(fsid, this));
1514 
1515     fsid_it = r.first;
1516 
1517     if (!r.second)
1518     {
1519         // remove previous fsid assignment (the node is likely about to be deleted)
1520         fsid_it->second->fsid_it = fsidnodes.end();
1521         fsid_it->second = this;
1522     }
1523 }
1524 
~LocalNode()1525 LocalNode::~LocalNode()
1526 {
1527     if (!sync)
1528     {
1529         LOG_err << "LocalNode::init() was never called";
1530         assert(false);
1531         return;
1532     }
1533 
1534     if (sync->state == SYNC_ACTIVE || sync->state == SYNC_INITIALSCAN)
1535     {
1536         sync->statecachedel(this);
1537 
1538         if (type == FOLDERNODE)
1539         {
1540             sync->client->app->syncupdate_local_folder_deletion(sync, this);
1541         }
1542         else
1543         {
1544             sync->client->app->syncupdate_local_file_deletion(sync, this);
1545         }
1546     }
1547 
1548     setnotseen(0);
1549 
1550     newnode.reset();
1551 
1552     if (sync->dirnotify.get())
1553     {
1554         // deactivate corresponding notifyq records
1555         for (int q = DirNotify::RETRY; q >= DirNotify::EXTRA; q--)
1556         {
1557             sync->dirnotify->notifyq[q].replaceLocalNodePointers(this, (LocalNode*)~0);
1558         }
1559     }
1560 
1561     // remove from fsidnode map, if present
1562     if (fsid_it != sync->client->fsidnode.end())
1563     {
1564         sync->client->fsidnode.erase(fsid_it);
1565     }
1566 
1567     sync->client->totalLocalNodes--;
1568     sync->localnodes[type]--;
1569 
1570     if (type == FILENODE && size > 0)
1571     {
1572         sync->localbytes -= size;
1573     }
1574 
1575     if (type == FOLDERNODE)
1576     {
1577         if (sync->dirnotify.get())
1578         {
1579             sync->dirnotify->delnotify(this);
1580         }
1581     }
1582 
1583     // remove parent association
1584     if (parent)
1585     {
1586         setnameparent(NULL, NULL, NULL);
1587     }
1588 
1589     for (localnode_map::iterator it = children.begin(); it != children.end(); )
1590     {
1591         delete it++->second;
1592     }
1593 
1594     if (node)
1595     {
1596         // move associated node to SyncDebris unless the sync is currently
1597         // shutting down
1598         if (sync->state < SYNC_INITIALSCAN)
1599         {
1600             node->localnode = NULL;
1601         }
1602         else
1603         {
1604             sync->client->movetosyncdebris(node, sync->inshare);
1605         }
1606     }
1607 
1608     slocalname.reset();
1609 }
1610 
getLocalPath(bool sdisable) const1611 LocalPath LocalNode::getLocalPath(bool sdisable) const
1612 {
1613     LocalPath lp;
1614     getlocalpath(lp, sdisable);
1615     return lp;
1616 }
1617 
getlocalpath(LocalPath & path,bool sdisable,const std::string * localseparator) const1618 void LocalNode::getlocalpath(LocalPath& path, bool sdisable, const std::string* localseparator) const
1619 {
1620     if (!sync)
1621     {
1622         LOG_err << "LocalNode::init() was never called";
1623         assert(false);
1624         return;
1625     }
1626 
1627     path.erase();
1628 
1629     for (const LocalNode* l = this; l != nullptr; l = l->parent)
1630     {
1631         assert(!l->parent || l->parent->sync == sync);
1632 
1633         // use short name, if available (less likely to overflow MAXPATH,
1634         // perhaps faster?) and sdisable not set.  Use localname from the sync root though, as it has the absolute path.
1635         if (!sdisable && l->slocalname && l->parent)
1636         {
1637             path.prependWithSeparator(*l->slocalname, localseparator ? *localseparator : sync->client->fsaccess->localseparator);
1638         }
1639         else
1640         {
1641             path.prependWithSeparator(l->localname, localseparator ? *localseparator : sync->client->fsaccess->localseparator);
1642         }
1643     }
1644 }
1645 
localnodedisplaypath(FileSystemAccess & fsa) const1646 string LocalNode::localnodedisplaypath(FileSystemAccess& fsa) const
1647 {
1648     LocalPath local;
1649     getlocalpath(local, true);
1650     return local.toPath(fsa);
1651 }
1652 
1653 // locate child by localname or slocalname
childbyname(LocalPath * localname)1654 LocalNode* LocalNode::childbyname(LocalPath* localname)
1655 {
1656     localnode_map::iterator it;
1657 
1658     if (!localname || ((it = children.find(localname)) == children.end() && (it = schildren.find(localname)) == schildren.end()))
1659     {
1660         return NULL;
1661     }
1662 
1663     return it->second;
1664 }
1665 
prepare()1666 void LocalNode::prepare()
1667 {
1668     getlocalpath(transfer->localfilename, true);
1669 
1670     // is this transfer in progress? update file's filename.
1671     if (transfer->slot && transfer->slot->fa && !transfer->slot->fa->nonblocking_localname.empty())
1672     {
1673         transfer->slot->fa->updatelocalname(transfer->localfilename);
1674     }
1675 
1676     treestate(TREESTATE_SYNCING);
1677 }
1678 
1679 // complete a sync upload: complete to //bin if a newer node exists (which
1680 // would have been caused by a race condition)
completed(Transfer * t,LocalNode *)1681 void LocalNode::completed(Transfer* t, LocalNode*)
1682 {
1683     // complete to rubbish for later retrieval if the parent node does not
1684     // exist or is newer
1685     if (!parent || !parent->node || (node && mtime < node->mtime))
1686     {
1687         h = t->client->rootnodes[RUBBISHNODE - ROOTNODE];
1688     }
1689     else
1690     {
1691         // otherwise, overwrite node if it already exists and complete in its
1692         // place
1693         h = parent->node->nodehandle;
1694     }
1695 
1696     File::completed(t, this);
1697 }
1698 
1699 // serialize/unserialize the following LocalNode properties:
1700 // - type/size
1701 // - fsid
1702 // - parent LocalNode's dbid
1703 // - corresponding Node handle
1704 // - local name
1705 // - fingerprint crc/mtime (filenodes only)
serialize(string * d)1706 bool LocalNode::serialize(string* d)
1707 {
1708     CacheableWriter w(*d);
1709     w.serializei64(type ? -type : size);
1710     w.serializehandle(fsid);
1711     w.serializeu32(parent ? parent->dbid : 0);
1712     w.serializenodehandle(node ? node->nodehandle : UNDEF);
1713     w.serializestring(*localname.editStringDirect());
1714     if (type == FILENODE)
1715     {
1716         w.serializebinary((byte*)crc.data(), sizeof(crc));
1717         w.serializecompressed64(mtime);
1718     }
1719     w.serializebyte(mSyncable);
1720     w.serializeexpansionflags(1);  // first flag indicates we are storing slocalname.  Storing it is much, much faster than looking it up on startup.
1721     w.serializepstr(slocalname ? slocalname->editStringDirect() : nullptr);
1722     return true;
1723 }
1724 
unserialize(Sync * sync,const string * d)1725 LocalNode* LocalNode::unserialize(Sync* sync, const string* d)
1726 {
1727     if (d->size() < sizeof(m_off_t)         // type/size combo
1728                   + sizeof(handle)          // fsid
1729                   + sizeof(uint32_t)        // parent dbid
1730                   + MegaClient::NODEHANDLE  // handle
1731                   + sizeof(short))          // localname length
1732     {
1733         LOG_err << "LocalNode unserialization failed - short data";
1734         return NULL;
1735     }
1736 
1737     CacheableReader r(*d);
1738 
1739     nodetype_t type;
1740     m_off_t size;
1741 
1742     if (!r.unserializei64(size)) return nullptr;
1743 
1744     if (size < 0 && size >= -FOLDERNODE)
1745     {
1746         // will any compiler optimize this to a const assignment?
1747         type = (nodetype_t)-size;
1748         size = 0;
1749     }
1750     else
1751     {
1752         type = FILENODE;
1753     }
1754 
1755     handle fsid;
1756     uint32_t parent_dbid;
1757     handle h = 0;
1758     string localname, shortname;
1759     uint64_t mtime = 0;
1760     int32_t crc[4];
1761     memset(crc, 0, sizeof crc);
1762     byte syncable = 1;
1763     unsigned char expansionflags[8] = { 0 };
1764 
1765     if (!r.unserializehandle(fsid) ||
1766         !r.unserializeu32(parent_dbid) ||
1767         !r.unserializenodehandle(h) ||
1768         !r.unserializestring(localname) ||
1769         (type == FILENODE && !r.unserializebinary((byte*)crc, sizeof(crc))) ||
1770         (type == FILENODE && !r.unserializecompressed64(mtime)) ||
1771         (r.hasdataleft() && !r.unserializebyte(syncable)) ||
1772         (r.hasdataleft() && !r.unserializeexpansionflags(expansionflags, 1)) ||
1773         (expansionflags[0] && !r.unserializecstr(shortname, false)))
1774     {
1775         LOG_err << "LocalNode unserialization failed at field " << r.fieldnum;
1776         return nullptr;
1777     }
1778     assert(!r.hasdataleft());
1779 
1780     LocalNode* l = new LocalNode();
1781 
1782     l->type = type;
1783     l->size = size;
1784 
1785     l->parent_dbid = parent_dbid;
1786 
1787     l->fsid = fsid;
1788     l->fsid_it = sync->client->fsidnode.end();
1789 
1790     l->localname = LocalPath(std::move(localname));
1791     l->slocalname.reset(shortname.empty() ? nullptr : new LocalPath(std::move(shortname)));
1792     l->slocalname_in_db = 0 != expansionflags[0];
1793     l->name = l->localname.toName(*sync->client->fsaccess);
1794 
1795     memcpy(l->crc.data(), crc, sizeof crc);
1796     l->mtime = mtime;
1797     l->isvalid = true;
1798 
1799     l->node = sync->client->nodebyhandle(h);
1800     l->parent = nullptr;
1801     l->sync = sync;
1802     l->mSyncable = syncable == 1;
1803 
1804     // FIXME: serialize/unserialize
1805     l->created = false;
1806     l->reported = false;
1807     l->checked = h != UNDEF; // TODO: Is this a bug? h will never be UNDEF
1808 
1809     return l;
1810 }
1811 
1812 #endif
1813 
newnode(Node * n)1814 void Fingerprints::newnode(Node* n)
1815 {
1816     if (n->type == FILENODE)
1817     {
1818         n->fingerprint_it = mFingerprints.end();
1819     }
1820 }
1821 
add(Node * n)1822 void Fingerprints::add(Node* n)
1823 {
1824     if (n->type == FILENODE)
1825     {
1826         n->fingerprint_it = mFingerprints.insert(n);
1827         mSumSizes += n->size;
1828     }
1829 }
1830 
remove(Node * n)1831 void Fingerprints::remove(Node* n)
1832 {
1833     if (n->type == FILENODE && n->fingerprint_it != mFingerprints.end())
1834     {
1835         mSumSizes -= n->size;
1836         mFingerprints.erase(n->fingerprint_it);
1837         n->fingerprint_it = mFingerprints.end();
1838     }
1839 }
1840 
clear()1841 void Fingerprints::clear()
1842 {
1843     mFingerprints.clear();
1844     mSumSizes = 0;
1845 }
1846 
getSumSizes()1847 m_off_t Fingerprints::getSumSizes()
1848 {
1849     return mSumSizes;
1850 }
1851 
nodebyfingerprint(FileFingerprint * fingerprint)1852 Node* Fingerprints::nodebyfingerprint(FileFingerprint* fingerprint)
1853 {
1854     fingerprint_set::iterator it = mFingerprints.find(fingerprint);
1855     return it == mFingerprints.end() ? nullptr : static_cast<Node*>(*it);
1856 }
1857 
nodesbyfingerprint(FileFingerprint * fingerprint)1858 node_vector *Fingerprints::nodesbyfingerprint(FileFingerprint* fingerprint)
1859 {
1860     node_vector *nodes = new node_vector();
1861     auto p = mFingerprints.equal_range(fingerprint);
1862     for (iterator it = p.first; it != p.second; ++it)
1863     {
1864         nodes->push_back(static_cast<Node*>(*it));
1865     }
1866     return nodes;
1867 }
1868 
1869 } // namespace
1870