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