Skip to main content

lockbook_server_lib/
file_service.rs

1use crate::ServerError;
2use crate::ServerError::ClientError;
3use crate::billing::app_store_client::AppStoreClient;
4use crate::billing::google_play_client::GooglePlayClient;
5use crate::billing::stripe_client::StripeClient;
6use crate::defense::SERVER_BANDWIDTH_CAP;
7use crate::document_service::DocumentService;
8use crate::schema::ServerDb;
9
10use crate::{RequestContext, ServerState};
11use db_rs::Db;
12use lb_rs::model::api::{UpsertError, *};
13use lb_rs::model::clock::get_time;
14use lb_rs::model::errors::{LbErrKind, LbResult};
15use lb_rs::model::file_like::FileLike;
16use lb_rs::model::file_metadata::{Diff, Owner};
17use lb_rs::model::server_meta::{IntoServerMeta, ServerMeta};
18use lb_rs::model::server_tree::ServerTree;
19use lb_rs::model::tree_like::TreeLike;
20use std::collections::{HashMap, HashSet};
21use std::hash::Hash;
22use std::ops::DerefMut;
23use tracing::{debug, error, warn};
24
25impl<S, A, G, D> ServerState<S, A, G, D>
26where
27    S: StripeClient,
28    A: AppStoreClient,
29    G: GooglePlayClient,
30    D: DocumentService,
31{
32    pub async fn upsert_file_metadata_v2(
33        &self, context: RequestContext<UpsertRequestV2>,
34    ) -> Result<(), ServerError<UpsertError>> {
35        let request = context.request;
36        let req_owner = Owner(context.public_key);
37
38        {
39            let mut prior_deleted = HashSet::new();
40            let mut current_deleted = HashSet::new();
41
42            let mut lock = self.index_db.lock().await;
43            let db = lock.deref_mut();
44            let tx = db.begin_transaction()?;
45
46            let usage_cap =
47                Self::get_cap(db, &context.public_key).map_err(|err| internal!("{:?}", err))?;
48
49            let mut tree = ServerTree::new(
50                req_owner,
51                &mut db.owned_files,
52                &mut db.shared_files,
53                &mut db.file_children,
54                &mut db.metas,
55            )?
56            .to_lazy();
57
58            let old_usage = Self::get_usage_helper(&mut tree)
59                .map_err(|err| internal!("{:?}", err))?
60                .iter()
61                .map(|f| f.size_bytes)
62                .sum::<u64>();
63
64            for id in tree.ids() {
65                if tree.calculate_deleted(&id)? {
66                    prior_deleted.insert(id);
67                }
68            }
69
70            let mut tree = tree.stage_diff_v2(request.updates.clone())?;
71            for id in tree.ids() {
72                if tree.calculate_deleted(&id)? {
73                    current_deleted.insert(id);
74                }
75            }
76
77            tree.validate(req_owner)?;
78
79            let new_usage = Self::get_usage_helper(&mut tree)
80                .map_err(|err| internal!("{:?}", err))?
81                .iter()
82                .map(|f| f.size_bytes)
83                .sum::<u64>();
84
85            debug!(?old_usage, ?new_usage, ?usage_cap, "usage caps on upsert");
86
87            if new_usage > usage_cap && new_usage >= old_usage {
88                warn!("user over cap");
89                return Err(ClientError(UpsertError::UsageIsOverDataCap));
90            }
91
92            let tree = tree.promote()?;
93
94            for id in tree.ids() {
95                if tree.find(&id)?.is_document()
96                    && current_deleted.contains(&id)
97                    && !prior_deleted.contains(&id)
98                {
99                    let meta = tree.find(&id)?;
100                    if let Some(hmac) = meta.file.timestamped_value.value.document_hmac().copied() {
101                        db.scheduled_file_cleanups
102                            .insert((*meta.id(), hmac), get_time().0)?;
103                    }
104                }
105            }
106
107            let all_files: Vec<ServerMeta> = tree.all_files()?.into_iter().cloned().collect();
108            for meta in all_files {
109                let id = meta.id();
110                if current_deleted.contains(id) && !prior_deleted.contains(id) {
111                    for user_access_info in meta.user_access_keys() {
112                        db.shared_files
113                            .remove(&Owner(user_access_info.encrypted_for), id)?;
114                    }
115                }
116            }
117
118            db.last_seen.insert(req_owner, get_time().0 as u64)?;
119
120            tx.drop_safely()?;
121        }
122
123        Ok(())
124    }
125
126    pub async fn change_doc_v2(
127        &self, context: RequestContext<ChangeDocRequestV2>,
128    ) -> Result<(), ServerError<ChangeDocError>> {
129        use ChangeDocError::*;
130        let owner = Owner(context.public_key);
131        let request = context.request;
132        let id = *request.diff.id();
133
134        // Validate Diff
135        if request.diff.diff() != vec![Diff::Hmac] {
136            return Err(ClientError(DiffMalformed));
137        }
138
139        match request.diff.new.timestamped_value.value.doc_size() {
140            Some(size) => {
141                if *size != request.new_content.value.len() {
142                    return Err(ClientError(NewSizeIncorrect));
143                }
144            }
145            None => {
146                // do we even want to support this?
147                if !request.new_content.value.is_empty() {
148                    return Err(ClientError(NewSizeIncorrect));
149                }
150            }
151        }
152
153        let hmac = if let Some(hmac) = request.diff.new.document_hmac() {
154            base64::encode_config(hmac, base64::URL_SAFE)
155        } else {
156            return Err(ClientError(HmacMissing));
157        };
158
159        let req_pk = context.public_key;
160
161        {
162            let mut lock = self.index_db.lock().await;
163            let db = lock.deref_mut();
164            let usage_cap = Self::get_cap(db, &context.public_key)
165                .map_err(|err| internal!("{:?}", err))? as usize;
166
167            let meta = db
168                .metas
169                .get()
170                .get(request.diff.new.id())
171                .ok_or(ClientError(DocumentNotFound))?
172                .clone();
173
174            let mut tree = ServerTree::new(
175                owner,
176                &mut db.owned_files,
177                &mut db.shared_files,
178                &mut db.file_children,
179                &mut db.metas,
180            )?
181            .to_lazy();
182
183            let old_usage = Self::get_usage_helper(&mut tree)
184                .map_err(|err| internal!("{:?}", err))?
185                .iter()
186                .map(|f| f.size_bytes)
187                .sum::<u64>() as usize;
188            let old_size = *meta.file.timestamped_value.value.doc_size();
189
190            let new_size = request.new_content.value.len();
191
192            let new_usage = old_usage - old_size.unwrap_or_default() + new_size;
193
194            debug!(?old_usage, ?new_usage, ?usage_cap, "usage caps on change doc");
195
196            if new_usage > usage_cap {
197                warn!("user over cap");
198                return Err(ClientError(UsageIsOverDataCap));
199            }
200
201            let meta_owner = meta.owner();
202
203            let direct_access = meta_owner.0 == req_pk;
204
205            if tree.maybe_find(request.diff.new.id()).is_none() {
206                return Err(ClientError(NotPermissioned));
207            }
208
209            let mut share_access = false;
210            if !direct_access {
211                for ancestor in tree
212                    .ancestors(request.diff.id())?
213                    .iter()
214                    .chain(vec![request.diff.new.id()])
215                {
216                    let meta = tree.find(ancestor)?;
217
218                    if meta
219                        .user_access_keys()
220                        .iter()
221                        .any(|access| access.encrypted_for == req_pk)
222                    {
223                        share_access = true;
224                        break;
225                    }
226                }
227            }
228
229            if !direct_access && !share_access {
230                return Err(ClientError(NotPermissioned));
231            }
232
233            let meta = &tree
234                .maybe_find(request.diff.new.id())
235                .ok_or(ClientError(DocumentNotFound))?
236                .file;
237
238            if let Some(old) = &request.diff.old {
239                if meta != old {
240                    return Err(ClientError(OldVersionIncorrect));
241                }
242            }
243
244            if tree.calculate_deleted(request.diff.new.id())? {
245                return Err(ClientError(DocumentDeleted));
246            }
247
248            let id = request.diff.new.id();
249            let hmac = request.diff.new.document_hmac().unwrap();
250            db.scheduled_file_cleanups.remove(&(*id, *hmac))?;
251        }
252
253        let new_version = get_time().0 as u64;
254        let new = request.diff.new.clone().add_time(new_version);
255        self.document_service
256            .insert(
257                request.diff.new.id(),
258                request.diff.new.document_hmac().unwrap(),
259                &request.new_content,
260            )
261            .await?;
262        debug!(?id, ?hmac, "Inserted document contents");
263
264        let result = async {
265            let mut lock = self.index_db.lock().await;
266            let db = lock.deref_mut();
267            let tx = db.begin_transaction()?;
268
269            let mut tree = ServerTree::new(
270                owner,
271                &mut db.owned_files,
272                &mut db.shared_files,
273                &mut db.file_children,
274                &mut db.metas,
275            )?
276            .to_lazy();
277
278            if tree.calculate_deleted(request.diff.new.id())? {
279                return Err(ClientError(DocumentDeleted));
280            }
281
282            let meta = &tree
283                .maybe_find(request.diff.new.id())
284                .ok_or(ClientError(DocumentNotFound))?
285                .file;
286
287            if let Some(old) = &request.diff.old {
288                if meta != old {
289                    return Err(ClientError(OldVersionIncorrect));
290                }
291            }
292
293            tree.stage(vec![new]).promote()?;
294            if let Some(old) = &request
295                .diff
296                .old
297                .and_then(|old| old.document_hmac().copied())
298            {
299                db.scheduled_file_cleanups
300                    .insert((*request.diff.new.id(), *old), get_time().0)?;
301            }
302
303            tx.drop_safely()?;
304            drop(lock);
305            Ok(())
306        };
307
308        let result = result.await;
309
310        if result.is_err() {
311            // Cleanup the NEW file created if, for some reason, the tx failed
312            self.document_service
313                .delete(request.diff.new.id(), request.diff.new.document_hmac().unwrap())
314                .await?;
315            debug!(?id, ?hmac, "Cleaned up new document contents after failed metadata update");
316        }
317
318        result?;
319
320        Ok(())
321    }
322
323    pub async fn get_document(
324        &self, context: RequestContext<GetDocRequest>,
325    ) -> Result<GetDocumentResponse, ServerError<GetDocumentError>> {
326        let request = &context.request;
327        let requester = Owner(context.public_key);
328        {
329            let mut lock = self.index_db.lock().await;
330            let db = lock.deref_mut();
331            let tx = db.begin_transaction()?;
332
333            let meta_exists = db.metas.get().get(&request.id).is_some();
334
335            let mut tree = ServerTree::new(
336                requester,
337                &mut db.owned_files,
338                &mut db.shared_files,
339                &mut db.file_children,
340                &mut db.metas,
341            )?
342            .to_lazy();
343
344            if tree.maybe_find(&request.id).is_none() {
345                return Err(if meta_exists {
346                    ClientError(GetDocumentError::NotPermissioned)
347                } else {
348                    ClientError(GetDocumentError::DocumentNotFound)
349                });
350            }
351
352            if tree.calculate_deleted(&request.id)? {
353                return Err(ClientError(GetDocumentError::DocumentNotFound));
354            }
355
356            tx.drop_safely()?;
357        };
358
359        let Some(content) = self
360            .document_service
361            .maybe_get(&request.id, &request.hmac)
362            .await?
363        else {
364            return Err(ClientError(GetDocumentError::DocumentNotFound));
365        };
366
367        let mut lock = self.index_db.lock().await;
368        let db = lock.deref_mut();
369        let tx = db.begin_transaction()?;
370
371        if self.config.features.bandwidth_controls {
372            let mut server_wide = db.server_egress.get().cloned().unwrap_or_default();
373            let mut account_bandwidth = db
374                .egress_by_owner
375                .get()
376                .get(&requester)
377                .cloned()
378                .unwrap_or_default();
379            let account_bandwidth_cap = db
380                .accounts
381                .get()
382                .get(&requester)
383                .map(|account| account.billing_info.bandwidth_cap())
384                .unwrap_or_default();
385
386            let doc_size = content.value.len();
387
388            if doc_size + server_wide.current_bandwidth() > SERVER_BANDWIDTH_CAP {
389                error!("Bandwidth caps are now being enforced");
390                if doc_size + account_bandwidth.current_bandwidth() > account_bandwidth_cap {
391                    error!("User bandwidth cap exceeded");
392                    return Err(ClientError(GetDocumentError::BandwidthExceeded));
393                }
394            }
395
396            server_wide.increase_by(doc_size);
397            account_bandwidth.increase_by(doc_size);
398
399            db.server_egress.insert(server_wide)?;
400            db.egress_by_owner.insert(requester, account_bandwidth)?;
401        }
402
403        tx.drop_safely()?;
404
405        Ok(GetDocumentResponse { content })
406    }
407
408    pub async fn get_file_ids(
409        &self, context: RequestContext<GetFileIdsRequest>,
410    ) -> Result<GetFileIdsResponse, ServerError<GetFileIdsError>> {
411        let owner = Owner(context.public_key);
412        let mut db = self.index_db.lock().await;
413        let db = db.deref_mut();
414
415        Ok(GetFileIdsResponse {
416            ids: ServerTree::new(
417                owner,
418                &mut db.owned_files,
419                &mut db.shared_files,
420                &mut db.file_children,
421                &mut db.metas,
422            )?
423            .ids()
424            .into_iter()
425            .collect(),
426        })
427    }
428
429    pub async fn get_updates_v2(
430        &self, context: RequestContext<GetUpdatesRequestV2>,
431    ) -> Result<GetUpdatesResponseV2, ServerError<GetUpdatesError>> {
432        let request = &context.request;
433        let owner = Owner(context.public_key);
434
435        let mut db = self.index_db.lock().await;
436        let db = db.deref_mut();
437        let mut tree = ServerTree::new(
438            owner,
439            &mut db.owned_files,
440            &mut db.shared_files,
441            &mut db.file_children,
442            &mut db.metas,
443        )?
444        .to_lazy();
445
446        let mut result_ids = HashSet::new();
447        for id in tree.ids() {
448            let file = tree.find(&id)?;
449            if file.version >= request.since_metadata_version {
450                result_ids.insert(id);
451                if file.owner() != owner
452                    && file
453                        .user_access_keys()
454                        .iter()
455                        .any(|k| !k.deleted && k.encrypted_for == context.public_key)
456                {
457                    result_ids.insert(id);
458                    result_ids.extend(tree.descendants(&id)?);
459                }
460            }
461        }
462
463        Ok(GetUpdatesResponseV2 {
464            as_of_metadata_version: get_time().0 as u64,
465            file_metadata: tree
466                .all_files()?
467                .into_iter()
468                .filter(|meta| result_ids.contains(meta.id()))
469                .map(|meta| meta.file.clone())
470                .collect(),
471        })
472    }
473
474    pub async fn admin_disappear_file(
475        &self, context: RequestContext<AdminDisappearFileRequest>,
476    ) -> Result<(), ServerError<AdminDisappearFileError>> {
477        let mut docs_to_delete = Vec::new();
478
479        {
480            let mut db = self.index_db.lock().await;
481            let db = db.deref_mut();
482            let tx = db.begin_transaction()?;
483
484            if !Self::is_admin::<AdminDisappearFileError>(
485                db,
486                &context.public_key,
487                &self.config.admin.admins,
488            )? {
489                return Err(ClientError(AdminDisappearFileError::NotPermissioned));
490            }
491
492            let owner = {
493                let meta = db
494                    .metas
495                    .get()
496                    .get(&context.request.id)
497                    .ok_or(ClientError(AdminDisappearFileError::FileNonexistent))?;
498                if meta.is_root() {
499                    return Err(ClientError(AdminDisappearFileError::RootModificationInvalid));
500                }
501                meta.owner()
502            };
503            let mut tree = ServerTree::new(
504                owner,
505                &mut db.owned_files,
506                &mut db.shared_files,
507                &mut db.file_children,
508                &mut db.metas,
509            )?
510            .to_lazy();
511
512            let metas_to_delete = {
513                let mut metas_to_delete = tree.descendants(&context.request.id)?;
514                metas_to_delete.insert(context.request.id);
515                metas_to_delete
516            };
517            for id in metas_to_delete.clone() {
518                if !tree.calculate_deleted(&id)? {
519                    let meta = tree.find(&id)?;
520                    if meta.is_document() && meta.owner() == owner {
521                        if let Some(hmac) = meta.document_hmac() {
522                            docs_to_delete.push((*meta.id(), *hmac));
523                        }
524                    }
525                }
526            }
527
528            for id in metas_to_delete {
529                let meta = db
530                    .metas
531                    .remove(&id)?
532                    .ok_or(ClientError(AdminDisappearFileError::FileNonexistent))?;
533
534                // maintain index: owned_files
535                let owner = meta.owner();
536
537                if !db.owned_files.remove(&owner, &id)? {
538                    error!(
539                        ?id,
540                        ?owner,
541                        "attempted to disappear a file, owner or id not present in owned_files"
542                    );
543                }
544
545                // maintain index: shared_files
546                for user_access_key in meta.user_access_keys() {
547                    let sharee = Owner(user_access_key.encrypted_for);
548                    if !db.shared_files.remove(&sharee, &id)? {
549                        error!(
550                            ?id,
551                            ?sharee,
552                            "attempted to disappear a file, a sharee didn't have it shared"
553                        );
554                    }
555                }
556
557                // maintain index: file_children
558                let parent = *meta.parent();
559                if !db.file_children.remove(meta.parent(), &id)? {
560                    error!(
561                        ?id,
562                        ?parent,
563                        "attempted to disappear a file, the parent didn't have it as a child"
564                    );
565                }
566            }
567
568            let username = db
569                .accounts
570                .get()
571                .get(&Owner(context.public_key))
572                .map(|account| account.username.clone())
573                .unwrap_or_else(|| "~unknown~".to_string());
574            warn!(?username, ?context.request.id, "Disappeared file");
575
576            tx.drop_safely()?;
577        }
578
579        for (id, version) in docs_to_delete {
580            self.document_service.delete(&id, &version).await?;
581        }
582
583        Ok(())
584    }
585
586    pub async fn admin_validate_account(
587        &self, context: RequestContext<AdminValidateAccountRequest>,
588    ) -> Result<AdminValidateAccount, ServerError<AdminValidateAccountError>> {
589        let request = &context.request;
590        let mut db = self.index_db.lock().await;
591        if !Self::is_admin::<AdminValidateAccountError>(
592            &db,
593            &context.public_key,
594            &self.config.admin.admins,
595        )? {
596            return Err(ClientError(AdminValidateAccountError::NotPermissioned));
597        }
598
599        let owner = *db
600            .usernames
601            .get()
602            .get(&request.username)
603            .ok_or(ClientError(AdminValidateAccountError::UserNotFound))?;
604
605        Ok(self.validate_account_helper(&mut db, owner)?)
606    }
607
608    pub fn validate_account_helper(
609        &self, db: &mut ServerDb, owner: Owner,
610    ) -> LbResult<AdminValidateAccount> {
611        let mut result = AdminValidateAccount::default();
612
613        let mut tree = ServerTree::new(
614            owner,
615            &mut db.owned_files,
616            &mut db.shared_files,
617            &mut db.file_children,
618            &mut db.metas,
619        )?
620        .to_lazy();
621
622        for id in tree.ids() {
623            if !tree.calculate_deleted(&id)? {
624                let file = tree.find(&id)?;
625                if file.is_document() && file.document_hmac().is_some() {
626                    if file.file.timestamped_value.value.doc_size().is_none() {
627                        result.documents_missing_size.push(id);
628                    }
629
630                    if !self
631                        .document_service
632                        .exists(&id, file.document_hmac().unwrap())
633                    {
634                        result.documents_missing_content.push(id);
635                    }
636                }
637            }
638        }
639
640        let validation_res = tree.stage(None).validate(owner);
641        match validation_res {
642            Ok(_) => {}
643            Err(err) => match err.kind {
644                LbErrKind::Validation(validation) => {
645                    result.tree_validation_failures.push(validation)
646                }
647                _ => {
648                    error!(?owner, ?err, "Unexpected error while validating tree")
649                }
650            },
651        }
652
653        Ok(result)
654    }
655
656    pub async fn admin_validate_server(
657        &self, context: RequestContext<AdminValidateServerRequest>,
658    ) -> Result<AdminValidateServer, ServerError<AdminValidateServerError>> {
659        let mut db = self.index_db.lock().await;
660        let db = db.deref_mut();
661
662        if !Self::is_admin::<AdminValidateServerError>(
663            db,
664            &context.public_key,
665            &self.config.admin.admins,
666        )? {
667            return Err(ClientError(AdminValidateServerError::NotPermissioned));
668        }
669
670        let mut result: AdminValidateServer = Default::default();
671
672        let mut deleted_ids = HashSet::new();
673        for (id, meta) in db.metas.get().clone() {
674            // todo: optimize
675            let mut tree = ServerTree::new(
676                meta.owner(),
677                &mut db.owned_files,
678                &mut db.shared_files,
679                &mut db.file_children,
680                &mut db.metas,
681            )?
682            .to_lazy();
683            if tree.calculate_deleted(&id)? {
684                deleted_ids.insert(id);
685            }
686        }
687
688        // validate accounts
689        for (owner, account) in db.accounts.get().clone() {
690            let validation = self.validate_account_helper(db, owner)?;
691            if !validation.is_empty() {
692                result
693                    .users_with_validation_failures
694                    .insert(account.username, validation);
695            }
696        }
697
698        // validate index: usernames
699        for (username, owner) in db.usernames.get().clone() {
700            if let Some(account) = db.accounts.get().get(&owner) {
701                if username != account.username {
702                    result
703                        .usernames_mapped_to_wrong_accounts
704                        .insert(username, account.username.clone());
705                }
706            } else {
707                result
708                    .usernames_mapped_to_nonexistent_accounts
709                    .insert(username, owner);
710            }
711        }
712        for (_, account) in db.accounts.get().clone() {
713            if db.usernames.get().get(&account.username).is_none() {
714                result
715                    .usernames_unmapped_to_accounts
716                    .insert(account.username.clone());
717            }
718        }
719
720        // validate index: owned_files
721        for (owner, ids) in db.owned_files.get().clone() {
722            for id in ids {
723                if let Some(meta) = db.metas.get().get(&id) {
724                    if meta.owner() != owner {
725                        insert(&mut result.owners_mapped_to_unowned_files, owner, id);
726                    }
727                } else {
728                    insert(&mut result.owners_mapped_to_nonexistent_files, owner, id);
729                }
730            }
731        }
732        for (id, meta) in db.metas.get().clone() {
733            if let Some(ids) = db.owned_files.get().get(&meta.owner()) {
734                if !ids.contains(&id) {
735                    insert(&mut result.owners_unmapped_to_owned_files, meta.owner(), *meta.id());
736                }
737            } else {
738                result.owners_unmapped.insert(meta.owner());
739            }
740        }
741
742        // validate index: shared_files
743        for (sharee, ids) in db.shared_files.get().clone() {
744            for id in ids {
745                if let Some(meta) = db.metas.get().get(&id) {
746                    if !meta.user_access_keys().iter().any(|k| {
747                        !k.deleted && k.encrypted_for == sharee.0 && k.encrypted_by != sharee.0
748                    }) {
749                        insert(&mut result.sharees_mapped_to_unshared_files, sharee, id);
750                    }
751                } else {
752                    insert(&mut result.sharees_mapped_to_nonexistent_files, sharee, id);
753                }
754                if deleted_ids.contains(&id) {
755                    insert(&mut result.sharees_mapped_for_deleted_files, sharee, id);
756                }
757            }
758        }
759        for (id, meta) in db.metas.get().clone() {
760            // check for implicit deletion (can't use server tree which depends on index)
761            let mut deleted = false;
762            let mut ancestor = meta.clone();
763            loop {
764                if ancestor.explicitly_deleted() {
765                    deleted = true;
766                    break;
767                }
768                if ancestor.is_root() {
769                    break;
770                }
771                match db.metas.get().get(ancestor.parent()) {
772                    Some(parent) => ancestor = parent.clone(),
773                    None => {
774                        error!("missing parent for file {:?}", ancestor.parent());
775                        deleted = true;
776                        break;
777                    }
778                }
779            }
780            if deleted {
781                continue;
782            }
783
784            for k in meta.user_access_keys() {
785                if k.deleted {
786                    continue;
787                }
788                let sharee = Owner(k.encrypted_for);
789                if let Some(ids) = db.shared_files.get().get(&sharee) {
790                    let self_share = k.encrypted_for == k.encrypted_by;
791                    let indexed_share = ids.contains(&id);
792                    if self_share && indexed_share {
793                        insert(&mut result.sharees_mapped_for_owned_files, sharee, id);
794                    } else if !self_share && !indexed_share {
795                        insert(&mut result.sharees_unmapped_to_shared_files, sharee, id);
796                    }
797                } else {
798                    result.sharees_unmapped.insert(meta.owner());
799                }
800            }
801        }
802
803        // validate index: file_children
804        for (parent_id, child_ids) in db.file_children.get().clone() {
805            for child_id in child_ids {
806                if let Some(meta) = db.metas.get().get(&child_id) {
807                    if meta.parent() != &parent_id {
808                        insert(
809                            &mut result.files_mapped_as_parent_to_non_children,
810                            parent_id,
811                            child_id,
812                        );
813                    }
814                } else {
815                    insert(
816                        &mut result.files_mapped_as_parent_to_nonexistent_children,
817                        parent_id,
818                        child_id,
819                    );
820                }
821            }
822        }
823        for (id, meta) in db.metas.get().clone() {
824            if let Some(child_ids) = db.file_children.get().get(meta.parent()) {
825                if meta.is_root() && child_ids.contains(&id) {
826                    result.files_mapped_as_parent_to_self.insert(id);
827                } else if !meta.is_root() && !child_ids.contains(&id) {
828                    insert(&mut result.files_unmapped_as_parent_to_children, *meta.parent(), id);
829                }
830            } else {
831                result.files_unmapped_as_parent.insert(*meta.parent());
832            }
833        }
834
835        // validate presence of documents
836        for (id, meta) in db.metas.get().clone() {
837            if let Some(hmac) = meta.document_hmac() {
838                if !deleted_ids.contains(&id) && !self.document_service.exists(&id, hmac) {
839                    result.files_with_hmacs_and_no_contents.insert(id);
840                }
841            }
842        }
843
844        Ok(result)
845    }
846
847    pub async fn admin_file_info(
848        &self, context: RequestContext<AdminFileInfoRequest>,
849    ) -> Result<AdminFileInfoResponse, ServerError<AdminFileInfoError>> {
850        let request = &context.request;
851        let mut db = self.index_db.lock().await;
852        let db = db.deref_mut();
853        if !Self::is_admin::<AdminFileInfoError>(
854            db,
855            &context.public_key,
856            &self.config.admin.admins,
857        )? {
858            return Err(ClientError(AdminFileInfoError::NotPermissioned));
859        }
860
861        let file = db
862            .metas
863            .get()
864            .get(&request.id)
865            .ok_or(ClientError(AdminFileInfoError::FileNonexistent))?
866            .clone();
867
868        let mut tree = ServerTree::new(
869            file.owner(),
870            &mut db.owned_files,
871            &mut db.shared_files,
872            &mut db.file_children,
873            &mut db.metas,
874        )?
875        .to_lazy();
876
877        let ancestors = tree
878            .ancestors(&request.id)?
879            .into_iter()
880            .filter_map(|id| tree.maybe_find(&id))
881            .cloned()
882            .collect();
883        let descendants = tree
884            .descendants(&request.id)?
885            .into_iter()
886            .filter_map(|id| tree.maybe_find(&id))
887            .cloned()
888            .collect();
889
890        Ok(AdminFileInfoResponse { file, ancestors, descendants })
891    }
892
893    pub async fn admin_rebuild_index(
894        &self, context: RequestContext<AdminRebuildIndexRequest>,
895    ) -> Result<(), ServerError<AdminRebuildIndexError>> {
896        let mut db = self.index_db.lock().await;
897
898        match context.request.index {
899            ServerIndex::OwnedFiles => {
900                db.owned_files.clear()?;
901                for owner in db.accounts.get().clone().keys() {
902                    db.owned_files.create_key(*owner)?;
903                }
904                for (id, file) in db.metas.get().clone() {
905                    db.owned_files.insert(file.owner(), id)?;
906                }
907            }
908            ServerIndex::SharedFiles => {
909                db.shared_files.clear()?;
910                for owner in db.accounts.get().clone().keys() {
911                    db.shared_files.create_key(*owner)?;
912                }
913                for (id, file) in db.metas.get().clone() {
914                    // check for implicit deletion (can't use server tree which depends on index)
915                    let mut deleted = false;
916                    let mut ancestor = file.clone();
917                    loop {
918                        if ancestor.explicitly_deleted() {
919                            deleted = true;
920                            break;
921                        }
922                        if ancestor.is_root() {
923                            break;
924                        }
925                        match db.metas.get().get(ancestor.parent()) {
926                            Some(parent) => ancestor = parent.clone(),
927                            None => {
928                                error!("missing parent for file {:?}", ancestor.parent());
929                                deleted = true;
930                                break;
931                            }
932                        }
933                    }
934
935                    if !deleted {
936                        for user_access_key in file.user_access_keys() {
937                            if !user_access_key.deleted
938                                && user_access_key.encrypted_for != user_access_key.encrypted_by
939                            {
940                                db.shared_files
941                                    .insert(Owner(user_access_key.encrypted_for), id)?;
942                            }
943                        }
944                    }
945                }
946            }
947            ServerIndex::FileChildren => {
948                db.file_children.clear()?;
949                for id in db.metas.get().clone().keys() {
950                    db.file_children.create_key(*id)?;
951                }
952                for (id, file) in db.metas.get().clone() {
953                    db.file_children.insert(*file.parent(), id)?;
954                }
955            }
956        }
957        Ok(())
958    }
959}
960
961fn insert<K: Hash + Eq, V: Hash + Eq>(map: &mut HashMap<K, HashSet<V>>, k: K, v: V) {
962    map.entry(k).or_default().insert(v);
963}