UNIXwork

Extended Attributes Teil 6: Solaris Syscalls

13. Dezember 2016

Programmiertechnisch sind Extended Attributes unter Solaris nur Dateien, daher gibt es keine speziellen Syscalls für den Zugriff darauf.

Eine Attribut-Datei öffnen kann man mit openat. Hierfür benötigt man nur einen File-Descriptor der Datei, von der man ein Attribut öffnen will. Es muss dann nur noch das O_XATTR-Flag zusätzlich angegeben werden. Man erhält einen gewöhnlichen File-Descriptor, aus dem wie gewohnt gelesen und geschrieben werden kann.

Hier ist ein kleines Beispielprogramm, dass ein Attribut ausliest und auf der Konsole ausgibt.

Die Liste aller Attribute erhält man, in dem man das Attribut-Verzeichnis ließt. Der Trick ist hier, dass man das "."-Verzeichnis relativ zur Datei mit dem O_XATTR-Flag öffnet.

int attrdir = openat(file, ".", O_RDONLY|O_XATTR);

Hier ist ein Beispiel dafür.

Für andere Dateisystemoperationen wie z.B. unlink oder chown gibt es ebenfalls *at-Funktionen, die man in Kombination mit einem File-Desriptor des Attribut-Verzeichnis benutzen kann. Siehe unlinkat, fstatat, fchownat.

Anstatt zuerst eine Datei mit open und danach mit openat das Attribut zu öffnen, kann man auch attropen benutzen, die das ganze in einer Funktion zusammenfasst.

Autor: Olaf | 0 Kommentare | Tags: solaris, xattr, c

Extended Attributes Teil 4: FreeBSD Syscalls

08. Dezember 2016

Um auf Extended Attributes in C zuzugreifen gibt es unter FreeBSD die extattr_*-Syscalls. Auch hier gibt es Varianten, die mit Pfaden arbeiten, welche für Filedeskriptoren und welche die im Falle eines symbolischen Links den Link selber betreffen.

Alle Funktionen erwarten als 2. Parameter ein int für den Namespace. Hierfür gibt es die Makros EXTATTR_NAMESPACE_USER und EXTATTR_NAMESPACE_SYSTEM.

Um an alle Namen der Attribute zu kommen gibt es die Funktion extattr_list_file (und noch die beiden anderen Varianten). Im Gegensatz zu Linux werden hier die Namen nicht einfach Null-terminiert hintereinander in den Buffer geschrieben, sondern vor jedem Namen steht zunächst ein einzelnes Bytes, welches die Länge des Namens angibt. Danach folgt der Name, jedoch ist dieser nicht Null-terminiert. Die anderen Funktionen sind alle recht trivial zu benutzen.

Da FreeBSD keine Tools hat, um Dateien mit ihren Extended Attributes zu kopieren, hab ich ein kleines Beispielprogramm geschrieben, dass alle Attribute aus dem User-Namespace von einer Datei auf eine andere kopiert.

Autor: Olaf | 0 Kommentare | Tags: bsd, xattr, c

Extended Attributes Teil 2: Linux Syscalls

06. Dezember 2016

Im vorherigen Teil ging es darum, wie man unter Linux Extended Attributes auf der Kommandozeile bearbeitet. Das Thema diesmal ist, mit welchen Syscalls man dies in C erledigen kann.

Für die 4 verschiedenen Operationen die man benötigt, nämlich listxattr, getxattr, setxattr und removexattr, gibt es jeweils 3 Syscalls.

#include <sys/types.h>
#include <sys/xattr.h>

ssize_t listxattr(const char *path, char *list, size_t size);
ssize_t llistxattr(const char *path, char *list, size_t size);
ssize_t flistxattr(int fd, char *list, size_t size);

ssize_t getxattr(const char *path, const char *name,
	             void *value, size_t size);
ssize_t lgetxattr(const char *path, const char *name,
	             void *value, size_t size);
ssize_t fgetxattr(int fd, const char *name,
	             void *value, size_t size);

int setxattr(const char *path, const char *name,
	             const void *value, size_t size, int flags);
int lsetxattr(const char *path, const char *name,
	             const void *value, size_t size, int flags);
int fsetxattr(int fd, const char *name,
	             const void *value, size_t size, int flags);

int removexattr(const char *path, const char *name);
int lremovexattr(const char *path, const char *name);
int fremovexattr(int fd, const char *name);

Die Syscalls mit dem l-Prefix liefern für den Fall dass path ein Symlink ist die Attribute des Symlinks selber, und nicht von der Datei, auf die der Symlink zeigt.

Bei den Syscalls mit dem f-Prefix übergibt man statt des Pfades einen Filedescriptor, den man mit open erhält.

Mit listxattr erhält man eine komplette Liste aller Extended Attributes für die Datei. Man muss einen vorher allozierten Buffer und seine Größe übergeben. Die Namen der Extended Attributes werden dann in diesen Buffer geschrieben, getrennt immer durch ein 0-Byte. Falls der Buffer zu klein ist, liefert die Funktion -1 zurück und setzt errno auf ERANGE. Man kann auch abfragen, wie groß der Buffer sein müsste, in dem man als size 0 übergibt. Der Rückgabewert ist dann die benötigte Größe des Buffers.

Hier ist ein Beispielprogramm, dass alle Extended Attributes einer Datei auflistet. Der Code um die Liste der Namen zu erhalten ist:

char *list = malloc(BUF_LEN);
ssize_t len = listxattr(path, list, BUF_LEN);

Wenn wir die Namen der Attribute haben, können wir den Wert mit getxattr erhalten. Diese Funktion erwartet ebenfalls einen Buffer, in den der Wert dann geschrieben wird. Auch hier kann man für size 0 angeben, um die Länge des Attributwerts zu erhalten.

Um das Beispielprogramm zu erweitern, dass es auch die Werte der Attribute ausgibt, könnte man die for-Schleife am Ende durch diesen Code ersetzen:

// print attributes
int begin = 0;
for(int i=0;i<len;i++) {
    if(list[i] == '\0') {
        char *xattr_name = list + begin;
        
        char value[0x10000];
        ssize_t vallen = getxattr(path, xattr_name, value, 0x10000);
        
        printf("%s: %.*s\n", xattr_name, (int)vallen, value);
        
        begin = i + 1;
    }
}

Es fehlt zwar die Fehlerbehandlung, es sollte jedoch immer funktionieren. Die Größe ist eh auf 64kb beschränkt, bei vielen Dateisystemen sogar noch weniger.

Kommen wir zu setxattr, mit dem man ein Extended Attribute setzt. Die Funktion erwartet als letzten Parameter ein flag. Ist dies 0, wird der Wert gesetzt, egal ob er schon existiert oder nicht. Bei XATTR_CREATE liefert die Funktion einen Fehler, wenn das Attribut schon existiert und bei XATTR_REPLACE gibt es einen Fehler, wenn der Wert nicht existiert.

xsetattr(path, "user.myattrname", value, valuelen, 0);

Bleibt zuletzt noch removexattr, dem man einfach nur den Dateipfad und den Attribut-Namen gibt, und der wird dann entfernt.

Autor: Olaf | 0 Kommentare | Tags: linux, xattr, c

WTF C

12. Oktober 2016

Was für eine Sprache ist dieser seltsam aussehender Code? Sieht aus wie C, aber irgendwas ist da falsch.

%:include <stdio.h>

int main(int argc, char **argv)
<%
	char str<::> = "World";
	printf("Hello %s!\n", str);
	return 0;
%>

Es handelt sich dabei tatsächlich um standardkonformes C.

Autor: Olaf | 0 Kommentare | Tags: c, wtf

Mehrere Wege führen zur FILE

15. September 2016

Die libc-Streams, repräsentiert durch FILE-Pointer, können nicht nur mit fopen erzeugt werden. Eine ganze Reihe an Funktionen erstellt ebenfalls diese Streams, die dann auch mit den stdio-Funktionen wie z.B. fprintf genutzt werden können.

fdopen

FILE *fdopen(int fd, const char *mode);

Mit fdopen erstellt man aus einem Unix-Filedescriptor ein FILE. Diese Funktion steht unter jedem POSIX-kompatiblen Betriebsystem zur Verfügung.

fmemopen

FILE *fmemopen(void *buf, size_t size, const char *mode);

Diese Funktion erstellt aus einem Buffer ein Stream. Der Buffer hat eine feste Größe, und gelangt der Stream an dessen Ende, wird nicht weiter geschrieben. Diese Funktion wurde durch die glibc eingeführt. Sie ist mitlerweile zwar im Posix-Standard, aber noch nicht überall verfügbar.

open_memstream

FILE *open_memstream(char **bufp, size_t *sizep);

Wie bei fmemopen erhält man mit dieser Funktion einen Stream für den Zugriff auf einen Buffer, allerdings wird dieser dynamisch mit malloc erstellt. Wenn nötig wird der Buffer auch automatisch vergrößert. Auch diese Funktion ist mitlerweile Posix-Standardisiert.

Beispiel:

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
	char *buf;
	size_t len;

	FILE *f = open_memstream(&buf, &len);

	fprintf(f, "test\n");
	fprintf(f, "%d\n", 5);
	fprintf(f, "%s\n", "hello world");

	fclose(f);

	printf("%s", buf);
	free(buf);

	return 0;
}

funopen (BSD)

FILE *
funopen(void *cookie, int (*readfn)(void *, char *, int),
	int (*writefn)(void *, const char *, int),
	off_t (*seekfn)(void *, off_t, int), int (*closefn)(void *));

Die BSDs haben die sehr mächtige Funktion funopen, die einen Stream anhand eigener I/O-Funktionen erstellt. Die I/O-Operationen, die durch die libc-Funktionen, wie z.B. fgetc, fputc, fprintf, usw., erzeugt werden, werden dann an die eigenen I/O-Funktionen, die man funopen übergeben hat, weitergeleitet. So kann man für praktisch alles was man sich vorstellen kann ein FILE*-kompatibles Interfaces erstellen.

Diese Funktion ist nur unter den BSD-Betriebsystemen verfügbar. NetBSD hat sogar die noch mächtigere Funktion funopen2.

fopencookie (glibc)

FILE *fopencookie(void *cookie, const char *mode,
                     cookie_io_functions_t io_funcs);

Die glibc folgt dem BSD-Vorbild, leider waren sie natürlich nicht in der Lage, das bereits bestehende Interface zu übernehmen. Trotzdem macht fopencookie genau das gleiche wie funopen. Man erzeugt einen Stream mit eigenen I/O-Funktionen. Der Unterschied ist hier, dass die Funktionspointer nicht einzelnd übergeben werden, sondern es wird eine Struct, die die Pointer enthält, übergeben.

Autor: Olaf | 0 Kommentare | Tags: c, unix
Zurück Weiter