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