Welchen Rückgriff hat man dafür?
Die Tatsache, dass GLIBC nicht #define USE_FCNTL_NOT_FCNTL64
viel sagen konnte. Ob richtig oder falsch, die meisten OS + Toolchain-Hersteller scheinen entschieden zu haben, dass das Targeting von Binärdateien für ältere Versionen ihrer Systeme von einer neueren Version keine hohe Priorität hat.
Der Weg des geringsten Widerstands besteht darin, eine virtuelle Maschine in der Nähe der ältesten OS + -Toolchain zu halten, die Ihr Projekt erstellt. Verwenden Sie diese Option, um Binärdateien zu erstellen, wenn Sie glauben, dass Binärdateien auf einem alten System ausgeführt werden.
Aber...
- Wenn Sie glauben, dass Ihre Verwendungen in der Teilmenge von fcntl () -Aufrufen enthalten sind, die von der Änderung der Offsetgröße nicht betroffen sind (dh, Sie verwenden keine Bytebereichssperren).
- ODER sind bereit, Ihren Code auf Offset-Fälle zu überprüfen, um eine abwärtskompatible Strukturdefinition zu verwenden
- UND haben keine Angst vor Voodoo
... dann lies weiter.
Der Name ist anders und fcntl ist variadic ohne ein vffcntl, das eine va_list nimmt. In solchen Situationen können Sie einen Aufruf einer variadischen Funktion nicht weiterleiten.
... um den genannten Wrapping-Trick anzuwenden , müssen Sie die Schnittstellendokumentation von fcntl () Zeile für Zeile durchgehen, die Variadic wie gewohnt entpacken und dann die Wrapped-Version mit einem neuen Variadic-Aufruf aufrufen.
Glücklicherweise ist es kein so schwieriger Fall (fcntl akzeptiert 0 oder 1 Argumente mit dokumentierten Typen). Hier ist der Code dafür, um anderen Ärger zu ersparen. Stellen Sie sicher, dass Sie --wrap = fcntl64 an den Linker übergeben ( -Wl, - wrap = fcntl64, wenn Sie ld nicht direkt aufrufen):
asm (".symver fcntl64, fcntl@GLIBC_2.2.5");
extern "C" int __wrap_fcntl64(int fd, int cmd, ...)
{
int result;
va_list va;
va_start(va, cmd);
switch (cmd) {
//
// File descriptor flags
//
case F_GETFD: goto takes_void;
case F_SETFD: goto takes_int;
// File status flags
//
case F_GETFL: goto takes_void;
case F_SETFL: goto takes_int;
// File byte range locking, not held across fork() or clone()
//
case F_SETLK: goto takes_flock_ptr_INCOMPATIBLE;
case F_SETLKW: goto takes_flock_ptr_INCOMPATIBLE;
case F_GETLK: goto takes_flock_ptr_INCOMPATIBLE;
// File byte range locking, held across fork()/clone() -- Not POSIX
//
case F_OFD_SETLK: goto takes_flock_ptr_INCOMPATIBLE;
case F_OFD_SETLKW: goto takes_flock_ptr_INCOMPATIBLE;
case F_OFD_GETLK: goto takes_flock_ptr_INCOMPATIBLE;
// Managing I/O availability signals
//
case F_GETOWN: goto takes_void;
case F_SETOWN: goto takes_int;
case F_GETOWN_EX: goto takes_f_owner_ex_ptr;
case F_SETOWN_EX: goto takes_f_owner_ex_ptr;
case F_GETSIG: goto takes_void;
case F_SETSIG: goto takes_int;
// Notified when process tries to open or truncate file (Linux 2.4+)
//
case F_SETLEASE: goto takes_int;
case F_GETLEASE: goto takes_void;
// File and directory change notification
//
case F_NOTIFY: goto takes_int;
// Changing pipe capacity (Linux 2.6.35+)
//
case F_SETPIPE_SZ: goto takes_int;
case F_GETPIPE_SZ: goto takes_void;
// File sealing (Linux 3.17+)
//
case F_ADD_SEALS: goto takes_int;
case F_GET_SEALS: goto takes_void;
// File read/write hints (Linux 4.13+)
//
case F_GET_RW_HINT: goto takes_uint64_t_ptr;
case F_SET_RW_HINT: goto takes_uint64_t_ptr;
case F_GET_FILE_RW_HINT: goto takes_uint64_t_ptr;
case F_SET_FILE_RW_HINT: goto takes_uint64_t_ptr;
default:
fprintf(stderr, "fcntl64 workaround got unknown F_XXX constant")
}
takes_void:
va_end(va);
return fcntl64(fd, cmd);
takes_int:
result = fcntl64(fd, cmd, va_arg(va, int));
va_end(va);
return result;
takes_flock_ptr_INCOMPATIBLE:
//
// !!! This is the breaking case: the size of the flock
// structure changed to accommodate larger files. If you
// need this, you'll have to define a compatibility struct
// with the older glibc and make your own entry point using it,
// then call fcntl64() with it directly (bear in mind that has
// been remapped to the old fcntl())
//
fprintf(stderr, "fcntl64 hack can't use glibc flock directly");
exit(1);
takes_f_owner_ex_ptr:
result = fcntl64(fd, cmd, va_arg(va, struct f_owner_ex*));
va_end(va);
return result;
takes_uint64_t_ptr:
result = fcntl64(fd, cmd, va_arg(va, uint64_t*));
va_end(va);
return result;
}
Beachten Sie, dass Sie abhängig von der Version, auf der Sie tatsächlich aufbauen, möglicherweise einige dieser Flaggenabschnitte #ifdef müssen, wenn sie nicht verfügbar sind.
Dies betrifft eine Vielzahl von Anwendungen ... Die Handbuchseite für fcntl () zeigt, dass dies der Einstiegspunkt für ein kleines Universum von Unterfunktionen ist
... und es sollte wahrscheinlich eine Lehre für die Menschen sein: Vermeiden Sie es, solche "Küchenspülen" -Funktionen durch unterschiedlichen Missbrauch zu schaffen.
fcntl64()
jetzt Fehler beim Zugriff auf Dateien mit mehr als 2 GB auftreten, wenn die Verwendung von gelöscht wird? Die einzige Möglichkeit, dies zu wissen, besteht darin, einen vollständigen Regressionstest für allefcntl()
Verwendungszwecke durchzuführen . Halten Sie eine virtuelle Maschine mit der ältesten OS + -Toolchain bereit, die Ihr Projekt erstellt. DAS ist die eigentliche Antwort, und IMO sollte ganz vorne mit dabei sein.Kompilieren Sie gegen eine ältere Version von
libc
. Zeitraum.Da glibc nicht vorwärtskompatibel ist , ist es nur abwärtskompatibel :
Ohne Garantie für die Vorwärtskompatibilität wissen Sie nicht, was sonst nicht richtig funktioniert .
quelle