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 old_usage = tree.calculate_usage(tree_owner)?;
274            let mut tree = tree.stage(vec![new_meta]);
275            let new_usage = tree.calculate_usage(tree_owner)?;
276            tree.validate(requester)?;
277            if new_usage > usage_cap && new_usage >= old_usage {
278                warn!("user over cap");
279                return Err(ClientError(UsageIsOverDataCap));
280            }
281            tree.promote()?;
282
283            if let Some(old_hmac) = diff.old.and_then(|old| old.document_hmac().copied()) {
284                db.scheduled_file_cleanups
285                    .insert((id, old_hmac), get_time().0)?;
286            }
287
288            tx.drop_safely()?;
289            drop(lock);
290            Ok(())
291        };
292
293        let result = result.await;
294
295        if result.is_err() {
296            // Cleanup the NEW file created if, for some reason, the tx failed
297            self.document_service.delete(&id, &hmac_bytes).await?;
298            debug!(?id, ?hmac, "Cleaned up new document contents after failed metadata update");
299        }
300
301        result?;
302
303        Ok(())
304    }
305
306    pub async fn get_document(
307        &self, context: RequestContext<GetDocRequest>,
308    ) -> Result<GetDocumentResponse, ServerError<GetDocumentError>> {
309        let request = &context.request;
310        let requester = Owner(context.public_key);
311        {
312            let mut lock = self.index_db.lock().await;
313            let db = lock.deref_mut();
314            let tx = db.begin_transaction()?;
315
316            let meta_exists = db.metas.get().get(&request.id).is_some();
317
318            let mut tree = ServerTree::new(
319                requester,
320                &mut db.owned_files,
321                &mut db.shared_files,
322                &mut db.file_children,
323                &mut db.metas,
324            )?
325            .to_lazy();
326
327            if tree.maybe_find(&request.id).is_none() {
328                return Err(if meta_exists {
329                    ClientError(GetDocumentError::NotPermissioned)
330                } else {
331                    ClientError(GetDocumentError::DocumentNotFound)
332                });
333            }
334
335            if tree.calculate_deleted(&request.id)? {
336                return Err(ClientError(GetDocumentError::DocumentNotFound));
337            }
338
339            tx.drop_safely()?;
340        };
341
342        let Some(content) = self
343            .document_service
344            .maybe_get(&request.id, &request.hmac)
345            .await?
346        else {
347            return Err(ClientError(GetDocumentError::DocumentNotFound));
348        };
349
350        let mut lock = self.index_db.lock().await;
351        let db = lock.deref_mut();
352        let tx = db.begin_transaction()?;
353
354        if self.config.features.bandwidth_controls {
355            let mut server_wide = db.server_egress.get().cloned().unwrap_or_default();
356            let mut account_bandwidth = db
357                .egress_by_owner
358                .get()
359                .get(&requester)
360                .cloned()
361                .unwrap_or_default();
362            let account_bandwidth_cap = db
363                .accounts
364                .get()
365                .get(&requester)
366                .map(|account| account.billing_info.bandwidth_cap())
367                .unwrap_or_default();
368
369            let doc_size = content.value.len();
370
371            if doc_size + server_wide.current_bandwidth() > SERVER_BANDWIDTH_CAP {
372                error!("Bandwidth caps are now being enforced");
373                if doc_size + account_bandwidth.current_bandwidth() > account_bandwidth_cap {
374                    error!("User bandwidth cap exceeded");
375                    return Err(ClientError(GetDocumentError::BandwidthExceeded));
376                }
377            }
378
379            server_wide.increase_by(doc_size);
380            account_bandwidth.increase_by(doc_size);
381
382            db.server_egress.insert(server_wide)?;
383            db.egress_by_owner.insert(requester, account_bandwidth)?;
384        }
385
386        tx.drop_safely()?;
387
388        Ok(GetDocumentResponse { content })
389    }
390
391    pub async fn get_file_ids(
392        &self, context: RequestContext<GetFileIdsRequest>,
393    ) -> Result<GetFileIdsResponse, ServerError<GetFileIdsError>> {
394        let owner = Owner(context.public_key);
395        let mut db = self.index_db.lock().await;
396        let db = db.deref_mut();
397
398        Ok(GetFileIdsResponse {
399            ids: ServerTree::new(
400                owner,
401                &mut db.owned_files,
402                &mut db.shared_files,
403                &mut db.file_children,
404                &mut db.metas,
405            )?
406            .ids()
407            .into_iter()
408            .collect(),
409        })
410    }
411
412    pub async fn get_updates_v2(
413        &self, context: RequestContext<GetUpdatesRequestV2>,
414    ) -> Result<GetUpdatesResponseV2, ServerError<GetUpdatesError>> {
415        let request = &context.request;
416        let owner = Owner(context.public_key);
417
418        let mut db = self.index_db.lock().await;
419        let db = db.deref_mut();
420        let mut tree = ServerTree::new(
421            owner,
422            &mut db.owned_files,
423            &mut db.shared_files,
424            &mut db.file_children,
425            &mut db.metas,
426        )?
427        .to_lazy();
428
429        let mut result_ids = HashSet::new();
430        for id in tree.ids() {
431            let file = tree.find(&id)?;
432            if file.version >= request.since_metadata_version {
433                result_ids.insert(id);
434                if file.owner() != owner
435                    && file
436                        .user_access_keys()
437                        .iter()
438                        .any(|k| !k.deleted && k.encrypted_for == context.public_key)
439                {
440                    result_ids.insert(id);
441                    result_ids.extend(tree.descendants(&id)?);
442                }
443            }
444        }
445
446        Ok(GetUpdatesResponseV2 {
447            as_of_metadata_version: get_time().0 as u64,
448            file_metadata: tree
449                .all_files()?
450                .into_iter()
451                .filter(|meta| result_ids.contains(meta.id()))
452                .map(|meta| meta.file.clone())
453                .collect(),
454        })
455    }
456
457    pub async fn admin_disappear_file(
458        &self, context: RequestContext<AdminDisappearFileRequest>,
459    ) -> Result<(), ServerError<AdminDisappearFileError>> {
460        let mut docs_to_delete = Vec::new();
461
462        {
463            let mut db = self.index_db.lock().await;
464            let db = db.deref_mut();
465            let tx = db.begin_transaction()?;
466
467            if !Self::is_admin::<AdminDisappearFileError>(
468                db,
469                &context.public_key,
470                &self.config.admin.admins,
471            )? {
472                return Err(ClientError(AdminDisappearFileError::NotPermissioned));
473            }
474
475            let owner = {
476                let meta = db
477                    .metas
478                    .get()
479                    .get(&context.request.id)
480                    .ok_or(ClientError(AdminDisappearFileError::FileNonexistent))?;
481                if meta.is_root() {
482                    return Err(ClientError(AdminDisappearFileError::RootModificationInvalid));
483                }
484                meta.owner()
485            };
486            let mut tree = ServerTree::new(
487                owner,
488                &mut db.owned_files,
489                &mut db.shared_files,
490                &mut db.file_children,
491                &mut db.metas,
492            )?
493            .to_lazy();
494
495            let metas_to_delete = {
496                let mut metas_to_delete = tree.descendants(&context.request.id)?;
497                metas_to_delete.insert(context.request.id);
498                metas_to_delete
499            };
500            for id in metas_to_delete.clone() {
501                if !tree.calculate_deleted(&id)? {
502                    let meta = tree.find(&id)?;
503                    if meta.is_document() && meta.owner() == owner {
504                        if let Some(hmac) = meta.document_hmac() {
505                            docs_to_delete.push((*meta.id(), *hmac));
506                        }
507                    }
508                }
509            }
510
511            for id in metas_to_delete {
512                let meta = db
513                    .metas
514                    .remove(&id)?
515                    .ok_or(ClientError(AdminDisappearFileError::FileNonexistent))?;
516
517                // maintain index: owned_files
518                let owner = meta.owner();
519
520                if !db.owned_files.remove(&owner, &id)? {
521                    error!(
522                        ?id,
523                        ?owner,
524                        "attempted to disappear a file, owner or id not present in owned_files"
525                    );
526                }
527
528                // maintain index: shared_files
529                for user_access_key in meta.user_access_keys() {
530                    let sharee = Owner(user_access_key.encrypted_for);
531                    if !db.shared_files.remove(&sharee, &id)? {
532                        error!(
533                            ?id,
534                            ?sharee,
535                            "attempted to disappear a file, a sharee didn't have it shared"
536                        );
537                    }
538                }
539
540                // maintain index: file_children
541                let parent = *meta.parent();
542                if !db.file_children.remove(meta.parent(), &id)? {
543                    error!(
544                        ?id,
545                        ?parent,
546                        "attempted to disappear a file, the parent didn't have it as a child"
547                    );
548                }
549            }
550
551            let username = db
552                .accounts
553                .get()
554                .get(&Owner(context.public_key))
555                .map(|account| account.username.clone())
556                .unwrap_or_else(|| "~unknown~".to_string());
557            warn!(?username, ?context.request.id, "Disappeared file");
558
559            tx.drop_safely()?;
560        }
561
562        for (id, version) in docs_to_delete {
563            self.document_service.delete(&id, &version).await?;
564        }
565
566        Ok(())
567    }
568
569    pub async fn admin_validate_account(
570        &self, context: RequestContext<AdminValidateAccountRequest>,
571    ) -> Result<AdminValidateAccount, ServerError<AdminValidateAccountError>> {
572        let request = &context.request;
573        let mut db = self.index_db.lock().await;
574        if !Self::is_admin::<AdminValidateAccountError>(
575            &db,
576            &context.public_key,
577            &self.config.admin.admins,
578        )? {
579            return Err(ClientError(AdminValidateAccountError::NotPermissioned));
580        }
581
582        let owner = *db
583            .usernames
584            .get()
585            .get(&request.username)
586            .ok_or(ClientError(AdminValidateAccountError::UserNotFound))?;
587
588        Ok(self.validate_account_helper(&mut db, owner)?)
589    }
590
591    pub fn validate_account_helper(
592        &self, db: &mut ServerDb, owner: Owner,
593    ) -> LbResult<AdminValidateAccount> {
594        let mut result = AdminValidateAccount::default();
595
596        let mut tree = ServerTree::new(
597            owner,
598            &mut db.owned_files,
599            &mut db.shared_files,
600            &mut db.file_children,
601            &mut db.metas,
602        )?
603        .to_lazy();
604
605        for id in tree.ids() {
606            if !tree.calculate_deleted(&id)? {
607                let file = tree.find(&id)?;
608                if file.is_document() && file.document_hmac().is_some() {
609                    if file.file.timestamped_value.value.doc_size().is_none() {
610                        result.documents_missing_size.push(id);
611                    }
612
613                    if !self
614                        .document_service
615                        .exists(&id, file.document_hmac().unwrap())
616                    {
617                        result.documents_missing_content.push(id);
618                    }
619                }
620            }
621        }
622
623        let validation_res = tree.stage(None).validate(owner);
624        match validation_res {
625            Ok(_) => {}
626            Err(err) => match err.kind {
627                LbErrKind::Validation(validation) => {
628                    result.tree_validation_failures.push(validation)
629                }
630                _ => {
631                    error!(?owner, ?err, "Unexpected error while validating tree")
632                }
633            },
634        }
635
636        Ok(result)
637    }
638
639    pub async fn admin_validate_server(
640        &self, context: RequestContext<AdminValidateServerRequest>,
641    ) -> Result<AdminValidateServer, ServerError<AdminValidateServerError>> {
642        let mut db = self.index_db.lock().await;
643        let db = db.deref_mut();
644
645        if !Self::is_admin::<AdminValidateServerError>(
646            db,
647            &context.public_key,
648            &self.config.admin.admins,
649        )? {
650            return Err(ClientError(AdminValidateServerError::NotPermissioned));
651        }
652
653        let mut result: AdminValidateServer = Default::default();
654
655        let mut deleted_ids = HashSet::new();
656        for (id, meta) in db.metas.get().clone() {
657            // todo: optimize
658            let mut tree = ServerTree::new(
659                meta.owner(),
660                &mut db.owned_files,
661                &mut db.shared_files,
662                &mut db.file_children,
663                &mut db.metas,
664            )?
665            .to_lazy();
666            if tree.calculate_deleted(&id)? {
667                deleted_ids.insert(id);
668            }
669        }
670
671        // validate accounts
672        for (owner, account) in db.accounts.get().clone() {
673            let validation = self.validate_account_helper(db, owner)?;
674            if !validation.is_empty() {
675                result
676                    .users_with_validation_failures
677                    .insert(account.username, validation);
678            }
679        }
680
681        // validate index: usernames
682        for (username, owner) in db.usernames.get().clone() {
683            if let Some(account) = db.accounts.get().get(&owner) {
684                if username != account.username {
685                    result
686                        .usernames_mapped_to_wrong_accounts
687                        .insert(username, account.username.clone());
688                }
689            } else {
690                result
691                    .usernames_mapped_to_nonexistent_accounts
692                    .insert(username, owner);
693            }
694        }
695        for (_, account) in db.accounts.get().clone() {
696            if db.usernames.get().get(&account.username).is_none() {
697                result
698                    .usernames_unmapped_to_accounts
699                    .insert(account.username.clone());
700            }
701        }
702
703        // validate index: owned_files
704        for (owner, ids) in db.owned_files.get().clone() {
705            for id in ids {
706                if let Some(meta) = db.metas.get().get(&id) {
707                    if meta.owner() != owner {
708                        insert(&mut result.owners_mapped_to_unowned_files, owner, id);
709                    }
710                } else {
711                    insert(&mut result.owners_mapped_to_nonexistent_files, owner, id);
712                }
713            }
714        }
715        for (id, meta) in db.metas.get().clone() {
716            if let Some(ids) = db.owned_files.get().get(&meta.owner()) {
717                if !ids.contains(&id) {
718                    insert(&mut result.owners_unmapped_to_owned_files, meta.owner(), *meta.id());
719                }
720            } else {
721                result.owners_unmapped.insert(meta.owner());
722            }
723        }
724
725        // validate index: shared_files
726        for (sharee, ids) in db.shared_files.get().clone() {
727            for id in ids {
728                if let Some(meta) = db.metas.get().get(&id) {
729                    if !meta.user_access_keys().iter().any(|k| {
730                        !k.deleted && k.encrypted_for == sharee.0 && k.encrypted_by != sharee.0
731                    }) {
732                        insert(&mut result.sharees_mapped_to_unshared_files, sharee, id);
733                    }
734                } else {
735                    insert(&mut result.sharees_mapped_to_nonexistent_files, sharee, id);
736                }
737                if deleted_ids.contains(&id) {
738                    insert(&mut result.sharees_mapped_for_deleted_files, sharee, id);
739                }
740            }
741        }
742        for (id, meta) in db.metas.get().clone() {
743            // check for implicit deletion (can't use server tree which depends on index)
744            let mut deleted = false;
745            let mut ancestor = meta.clone();
746            loop {
747                if ancestor.explicitly_deleted() {
748                    deleted = true;
749                    break;
750                }
751                if ancestor.is_root() {
752                    break;
753                }
754                match db.metas.get().get(ancestor.parent()) {
755                    Some(parent) => ancestor = parent.clone(),
756                    None => {
757                        error!("missing parent for file {:?}", ancestor.parent());
758                        deleted = true;
759                        break;
760                    }
761                }
762            }
763            if deleted {
764                continue;
765            }
766
767            for k in meta.user_access_keys() {
768                if k.deleted {
769                    continue;
770                }
771                let sharee = Owner(k.encrypted_for);
772                if let Some(ids) = db.shared_files.get().get(&sharee) {
773                    let self_share = k.encrypted_for == k.encrypted_by;
774                    let indexed_share = ids.contains(&id);
775                    if self_share && indexed_share {
776                        insert(&mut result.sharees_mapped_for_owned_files, sharee, id);
777                    } else if !self_share && !indexed_share {
778                        insert(&mut result.sharees_unmapped_to_shared_files, sharee, id);
779                    }
780                } else {
781                    result.sharees_unmapped.insert(meta.owner());
782                }
783            }
784        }
785
786        // validate index: file_children
787        for (parent_id, child_ids) in db.file_children.get().clone() {
788            for child_id in child_ids {
789                if let Some(meta) = db.metas.get().get(&child_id) {
790                    if meta.parent() != &parent_id {
791                        insert(
792                            &mut result.files_mapped_as_parent_to_non_children,
793                            parent_id,
794                            child_id,
795                        );
796                    }
797                } else {
798                    insert(
799                        &mut result.files_mapped_as_parent_to_nonexistent_children,
800                        parent_id,
801                        child_id,
802                    );
803                }
804            }
805        }
806        for (id, meta) in db.metas.get().clone() {
807            if let Some(child_ids) = db.file_children.get().get(meta.parent()) {
808                if meta.is_root() && child_ids.contains(&id) {
809                    result.files_mapped_as_parent_to_self.insert(id);
810                } else if !meta.is_root() && !child_ids.contains(&id) {
811                    insert(&mut result.files_unmapped_as_parent_to_children, *meta.parent(), id);
812                }
813            } else {
814                result.files_unmapped_as_parent.insert(*meta.parent());
815            }
816        }
817
818        // validate presence of documents
819        for (id, meta) in db.metas.get().clone() {
820            if let Some(hmac) = meta.document_hmac() {
821                if !deleted_ids.contains(&id) && !self.document_service.exists(&id, hmac) {
822                    result.files_with_hmacs_and_no_contents.insert(id);
823                }
824            }
825        }
826
827        Ok(result)
828    }
829
830    pub async fn admin_file_info(
831        &self, context: RequestContext<AdminFileInfoRequest>,
832    ) -> Result<AdminFileInfoResponse, ServerError<AdminFileInfoError>> {
833        let request = &context.request;
834        let mut db = self.index_db.lock().await;
835        let db = db.deref_mut();
836        if !Self::is_admin::<AdminFileInfoError>(
837            db,
838            &context.public_key,
839            &self.config.admin.admins,
840        )? {
841            return Err(ClientError(AdminFileInfoError::NotPermissioned));
842        }
843
844        let file = db
845            .metas
846            .get()
847            .get(&request.id)
848            .ok_or(ClientError(AdminFileInfoError::FileNonexistent))?
849            .clone();
850
851        let mut tree = ServerTree::new(
852            file.owner(),
853            &mut db.owned_files,
854            &mut db.shared_files,
855            &mut db.file_children,
856            &mut db.metas,
857        )?
858        .to_lazy();
859
860        let ancestors = tree
861            .ancestors(&request.id)?
862            .into_iter()
863            .filter_map(|id| tree.maybe_find(&id))
864            .cloned()
865            .collect();
866        let descendants = tree
867            .descendants(&request.id)?
868            .into_iter()
869            .filter_map(|id| tree.maybe_find(&id))
870            .cloned()
871            .collect();
872
873        Ok(AdminFileInfoResponse { file, ancestors, descendants })
874    }
875
876    pub async fn admin_rebuild_index(
877        &self, context: RequestContext<AdminRebuildIndexRequest>,
878    ) -> Result<(), ServerError<AdminRebuildIndexError>> {
879        let mut db = self.index_db.lock().await;
880
881        match context.request.index {
882            ServerIndex::OwnedFiles => {
883                db.owned_files.clear()?;
884                for owner in db.accounts.get().clone().keys() {
885                    db.owned_files.create_key(*owner)?;
886                }
887                for (id, file) in db.metas.get().clone() {
888                    db.owned_files.insert(file.owner(), id)?;
889                }
890            }
891            ServerIndex::SharedFiles => {
892                db.shared_files.clear()?;
893                for owner in db.accounts.get().clone().keys() {
894                    db.shared_files.create_key(*owner)?;
895                }
896                for (id, file) in db.metas.get().clone() {
897                    // check for implicit deletion (can't use server tree which depends on index)
898                    let mut deleted = false;
899                    let mut ancestor = file.clone();
900                    loop {
901                        if ancestor.explicitly_deleted() {
902                            deleted = true;
903                            break;
904                        }
905                        if ancestor.is_root() {
906                            break;
907                        }
908                        match db.metas.get().get(ancestor.parent()) {
909                            Some(parent) => ancestor = parent.clone(),
910                            None => {
911                                error!("missing parent for file {:?}", ancestor.parent());
912                                deleted = true;
913                                break;
914                            }
915                        }
916                    }
917
918                    if !deleted {
919                        for user_access_key in file.user_access_keys() {
920                            if !user_access_key.deleted
921                                && user_access_key.encrypted_for != user_access_key.encrypted_by
922                            {
923                                db.shared_files
924                                    .insert(Owner(user_access_key.encrypted_for), id)?;
925                            }
926                        }
927                    }
928                }
929            }
930            ServerIndex::FileChildren => {
931                db.file_children.clear()?;
932                for id in db.metas.get().clone().keys() {
933                    db.file_children.create_key(*id)?;
934                }
935                for (id, file) in db.metas.get().clone() {
936                    db.file_children.insert(*file.parent(), id)?;
937                }
938            }
939        }
940        Ok(())
941    }
942}
943
944fn insert<K: Hash + Eq, V: Hash + Eq>(map: &mut HashMap<K, HashSet<V>>, k: K, v: V) {
945    map.entry(k).or_default().insert(v);
946}