Getting {POSIXErrorDomain,18,'Cross-device link'}

Hi @priya.rajagopal,

I am getting Cross-device link error again and again while creating the database using Prebuilt database. Here is the error Log.

CouchbaseLiteException{POSIXErrorDomain,18,'Cross-device link'} 2020-10-25 16:35:54.306 4753-4753/com.drlite W/System.err: at com.couchbase.lite.internal.CBLStatus.convertException(CBLStatus.java:92) 2020-10-25 16:35:54.306 4753-4753/com.drlite W/System.err: at com.couchbase.lite.internal.CBLStatus.convertException(CBLStatus.java:46) 2020-10-25 16:35:54.306 4753-4753/com.drlite W/System.err: at com.couchbase.lite.AbstractDatabase.open(AbstractDatabase.java:1060) 2020-10-25 16:35:54.306 4753-4753/com.drlite W/System.err: at com.couchbase.lite.AbstractDatabase.<init>(AbstractDatabase.java:282) 2020-10-25 16:35:54.306 4753-4753/com.drlite W/System.err: at com.couchbase.lite.Database.<init>(Database.java:73) 2020-10-25 16:35:54.306 4753-4753/com.drlite W/System.err: at com.drlite.utils.DRXApplication.initMedicationDatabase(DRXApplication.java:421) 2020-10-25 16:35:54.306 4753-4753/com.drlite W/System.err: at com.drlite.utils.DRXApplication.initAllDatabase(DRXApplication.java:316) 2020-10-25 16:35:54.306 4753-4753/com.drlite W/System.err: at com.drlite.utils.DRXApplication.onCreate(DRXApplication.java:212) 2020-10-25 16:35:54.306 4753-4753/com.drlite W/System.err: at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1192) 2020-10-25 16:35:54.306 4753-4753/com.drlite W/System.err: at android.app.ActivityThread.handleBindApplication(ActivityThread.java:6712) 2020-10-25 16:35:54.307 4753-4753/com.drlite W/System.err: at android.app.ActivityThread.access$1300(ActivityThread.java:237) 2020-10-25 16:35:54.307 4753-4753/com.drlite W/System.err: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1913) 2020-10-25 16:35:54.307 4753-4753/com.drlite W/System.err: at android.os.Handler.dispatchMessage(Handler.java:106) 2020-10-25 16:35:54.307 4753-4753/com.drlite W/System.err: at android.os.Looper.loop(Looper.java:223) 2020-10-25 16:35:54.307 4753-4753/com.drlite W/System.err: at android.app.ActivityThread.main(ActivityThread.java:7656) 2020-10-25 16:35:54.307 4753-4753/com.drlite W/System.err: at java.lang.reflect.Method.invoke(Native Method) 2020-10-25 16:35:54.307 4753-4753/com.drlite W/System.err: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592) 2020-10-25 16:35:54.310 4753-4753/com.drlite W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947) 2020-10-25 16:35:54.310 4753-4753/com.drlite W/System.err: Caused by: LiteCoreException{domain=2, code=18, msg=Cross-device link} 2020-10-25 16:35:54.311 4753-4753/com.drlite W/System.err: at com.couchbase.lite.internal.core.C4Database.open(Native Method) 2020-10-25 16:35:54.311 4753-4753/com.drlite W/System.err: at com.couchbase.lite.internal.core.C4Database.<init>(C4Database.java:64) 2020-10-25 16:35:54.311 4753-4753/com.drlite W/System.err: at com.couchbase.lite.AbstractDatabase.open(AbstractDatabase.java:1045) 2020-10-25 16:35:54.311 4753-4753/com.drlite W/System.err: ... 15 more

This is the code snippet that I am using to load prebuilt database

DatabaseConfiguration configuration = new DatabaseConfiguration();
    if (!Database.exists(MEDICATION_DATABASE_NAME, getApplicationContext().getFilesDir())) {
        Log.e("medicine Database", "Not Exist");
        try {
            ZipUtils.unzip(getAssets().open("medicationdatabase.cblite2.zip"), getApplicationContext().getFilesDir());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

Please help me in what we are doing wrong. This code is working perfectly on android 10 or less. But above android 10 it is showing this error.

Regards
Achint Sarkar

So I can see several errors in your code and I know what the POSIX error is. I’m not sure I can connect them, though…

First of all, you need to use the directory that DatabaseConfiguration chooses. It is not necessarily getFilesDir. Like this:

if (Database.exists(MEDICATION_DATABASE_NAME, new File(config.getDirectory()))) { ...

Same deal unzipping it.

The error you are getting is caused by an attempt to create a hard link on one file system, to a file that is on another file system. I’m not clear on exactly who is doing that so I don’t know exactly what the problem is.

This code works on Android 11:

    DatabaseConfiguration config = new DatabaseConfiguration();
    if (Database.exists(DB_NAME, new File(config.getDirectory()))) { return; }

    File tmpDir = ctxt.getExternalFilesDir(TMP_DIR);
    if (tmpDir == null) { throw new IllegalStateException("Error creating temp dir"); }

    try (InputStream in = ctxt.getAssets().open(DB_ASSET)) { FileUtils.unzipToDir(in, tmpDir); }
    catch (IOException e) { throw new IllegalStateException("Failed unzipping db asset", e); }

Still Getting following error while running on the emulator
Failed to invalidate dentry files
2020-11-06 16:19:40.523 1302-1853/? V/FuseDaemon: do_lookup : parent_node = [node_path: /storage/180A-3F0D/3915979568/3915997424/3916072688/3916070672]
2020-11-06 16:19:40.523 1302-1853/? D/FuseDaemon: Node: 3916012400 created.
2020-11-06 16:19:40.523 1302-1853/? V/FuseDaemon: make_node_entry : node = [node_path: /storage/180A-3F0D/3915979568/3915997424/3916072688/3916070672/3916012400]
2020-11-06 16:19:40.523 1302-1610/? V/FuseDaemon: pf_access : node = [node_path: /storage/180A-3F0D/3915979568/3915997424/3916072688/3916070672/3916012400]
2020-11-06 16:19:40.527 6307-6307/com.drlite E/medicine Database: Exist
2020-11-06 16:19:40.529 1302-1853/? V/FuseDaemon: do_lookup : parent_node = [node_path: /storage/180A-3F0D/3915979568/3915997424]
2020-11-06 16:19:40.529 1302-1853/? V/FuseDaemon: make_node_entry : node = [node_path: /storage/180A-3F0D/3915979568/3915997424/3916072688]
2020-11-06 16:19:40.529 1302-1609/? V/FuseDaemon: do_lookup : parent_node = [node_path: /storage/180A-3F0D/3915979568/3915997424/3916072688]
2020-11-06 16:19:40.529 1302-6409/? E/FuseDaemon: Failed to invalidate dentry com.drlite
2020-11-06 16:19:40.530 1302-1853/? V/FuseDaemon: do_forget : node = [node_path: /storage/180A-3F0D/3915979568/3915997424/3916072688/3916070672/3916012400]
2020-11-06 16:19:40.530 1302-1853/? D/FuseDaemon: Node: 3916012400 deleted.
2020-11-06 16:19:40.530 1302-1853/? V/FuseDaemon: do_forget : node = [node_path: /storage/180A-3F0D/3915979568/3915997424/3916072688/3916070672]
2020-11-06 16:19:40.530 1302-1609/? V/FuseDaemon: make_node_entry : node = [node_path: /storage/180A-3F0D/3915979568/3915997424/3916072688/3916070672]
2020-11-06 16:19:40.530 1302-1853/? V/FuseDaemon: do_lookup : parent_node = [node_path: /storage/180A-3F0D/3915979568/3915997424/3916072688/3916070672]
2020-11-06 16:19:40.530 1302-1853/? D/FuseDaemon: Node: 3916012400 created.
2020-11-06 16:19:40.530 1302-1853/? V/FuseDaemon: make_node_entry : node = [node_path: /storage/180A-3F0D/3915979568/3915997424/3916072688/3916070672/3916012400]
2020-11-06 16:19:40.530 1302-6410/? E/FuseDaemon: Failed to invalidate dentry files
2020-11-06 16:19:40.531 1302-1610/? V/FuseDaemon: pf_access : node = [node_path: /storage/180A-3F0D/3915979568/3915997424/3916072688/3916070672/3916012400]
2020-11-06 16:19:40.692 6307-6307/com.drlite W/System.err: CouchbaseLiteException{POSIXErrorDomain,18,‘Cross-device link’}
2020-11-06 16:19:40.692 6307-6307/com.drlite W/System.err: at com.couchbase.lite.internal.CBLStatus.convertException(CBLStatus.java:92)
2020-11-06 16:19:40.692 6307-6307/com.drlite W/System.err: at com.couchbase.lite.internal.CBLStatus.convertException(CBLStatus.java:46)
2020-11-06 16:19:40.692 6307-6307/com.drlite W/System.err: at com.couchbase.lite.AbstractDatabase.open(AbstractDatabase.java:1060)
2020-11-06 16:19:40.692 6307-6307/com.drlite W/System.err: at com.couchbase.lite.AbstractDatabase.(AbstractDatabase.java:282)
2020-11-06 16:19:40.693 6307-6307/com.drlite W/System.err: at com.couchbase.lite.Database.(Database.java:73)
2020-11-06 16:19:40.693 6307-6307/com.drlite W/System.err: at com.drlite.utils.DRXApplication.initLabTestDatabase(DRXApplication.java:404)
2020-11-06 16:19:40.693 6307-6307/com.drlite W/System.err: at com.drlite.utils.DRXApplication.initAllDatabase(DRXApplication.java:314)
2020-11-06 16:19:40.693 6307-6307/com.drlite W/System.err: at com.drlite.activity.LoginActivity.onCreate(LoginActivity.java:82)
2020-11-06 16:19:40.693 6307-6307/com.drlite W/System.err: at android.app.Activity.performCreate(Activity.java:7995)
2020-11-06 16:19:40.693 6307-6307/com.drlite W/System.err: at android.app.Activity.performCreate(Activity.java:7979)
2020-11-06 16:19:40.693 6307-6307/com.drlite W/System.err: at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
2020-11-06 16:19:40.693 6307-6307/com.drlite W/System.err: at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3422)
2020-11-06 16:19:40.693 6307-6307/com.drlite W/System.err: at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
2020-11-06 16:19:40.693 6307-6307/com.drlite W/System.err: at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
2020-11-06 16:19:40.693 6307-6307/com.drlite W/System.err: at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
2020-11-06 16:19:40.693 6307-6307/com.drlite W/System.err: at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
2020-11-06 16:19:40.693 6307-6307/com.drlite W/System.err: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
2020-11-06 16:19:40.693 6307-6307/com.drlite W/System.err: at android.os.Handler.dispatchMessage(Handler.java:106)
2020-11-06 16:19:40.693 6307-6307/com.drlite W/System.err: at android.os.Looper.loop(Looper.java:223)
2020-11-06 16:19:40.693 6307-6307/com.drlite W/System.err: at android.app.ActivityThread.main(ActivityThread.java:7656)
2020-11-06 16:19:40.693 6307-6307/com.drlite W/System.err: at java.lang.reflect.Method.invoke(Native Method)
2020-11-06 16:19:40.693 6307-6307/com.drlite W/System.err: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
2020-11-06 16:19:40.693 6307-6307/com.drlite W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
2020-11-06 16:19:40.693 6307-6307/com.drlite W/System.err: Caused by: LiteCoreException{domain=2, code=18, msg=Cross-device link}
2020-11-06 16:19:40.693 6307-6307/com.drlite W/System.err: at com.couchbase.lite.internal.core.C4Database.open(Native Method)
2020-11-06 16:19:40.694 6307-6307/com.drlite W/System.err: at com.couchbase.lite.internal.core.C4Database.(C4Database.java:64)
2020-11-06 16:19:40.694 6307-6307/com.drlite W/System.err: at com.couchbase.lite.AbstractDatabase.open(AbstractDatabase.java:1045)
2020-11-06 16:19:40.694 6307-6307/com.drlite W/System.err: … 20 more

So… again… the problem is that something is trying to make a link (in the sense of *nix “ln”) from a file in one file system to a file in another file system. I’m struggling to figure out how this could happen.

Can you provide the following:

  • The canonical name of the database directory: new File(config.getDirectory()).getCanonicalPath().
  • The canonical name of the temp directory: CouchbaseLiteInternal.getTmpDirectoryPath() (Note that this is an internal method and must never be used in production code)
  • A diagram of the file system that includes both of the directories above.

Also, be aware that opening a database in an Activity is pretty strongly discouraged. It is an I/O operation and may take many millis, causing pretty awful jank.

I’m affraid its CouchbaseLite internal implementation issue.

We also encontered the issue as well in Pixel5 on android 11 and on emulator running Pixel_4_API_30.

For us it happens when we do following steps.

  • we bundle unencrypted initial db inside assets
  • we extract bundle to context.fileDirs
  • we open db with Database(SYNCED_DB_NAME, DatabaseConfiguration())
  • we try to encrypt db by applying db.changeEncryptionKey(aEncryptionKey) - CRASH happens here

I digged a bit deeper and found that DatabaseConfiguration holds reference to 2 directories : tmpDir and databaseDir.
I would assume that internaly some couchbase optimizations are being done to avoid copying like creating temp, populiating it with original db content, encrypting it, deleting original db and making finally hard link from tmp folder to original database localtion.

I guess it was working fine while tmpDir and filesDir were placed on the same linux device - which is apparently no longer a case (at least not for all android 11 devices)

The workarount for me was to configure DatabaseConfiguration.directory = context.cacheDir.absolutePath (as internal implementation shows that actually cacheDir is used as temp dir ),
creating db, (now its in the same device as tmp db) encrytping it closing it, then copying to its desired location.

Simple solution would be to allow configuration of temporary directory (its a private field currently) and adding documentation of the contract that it has to be the same linux device.

The ultimate solution would be to fix internals of CouchbaseLite to not rely on the tempDir and database dir to be placed on the same linux device.

Very glad that you have a workaround. It is something like what I would have suggested, if you had provided the information I requested.

The discussion in your post turns out to be stuff with which I, as the developer for the Android platform, am very, very familiar. As Android’s file system contract changes, we have to try to keep up with it. We are a bit behind 11, at this point.