Hlavní navigace

Programování pod Linuxem pro všechny (3)

Jakub Matys

V dnešním dílu si konečně povíme o vytváření dočasných souborů, bude se jednat o funkce využívající k zápisu a čtení dat deskriptor souboru i proměnnou typu *FILE.

V minulém dílu jsem pasáž o systémových proměnných napsal trochu stroze. Úplným začátečníkům se omlouvám. Napíšu zde ještě pár doplňujících informací.

Proměnné se dědí z procesu na proces, tzn. rodičovský proces předá spouštěnému procesu všechny své proměnné. Všechny proměnné, které jsou součástí prostředí (environment) procesu si můžete prohlédnout v souboru /proc/cislo-procesu/environ. Přidávat proměnné do prostředí (bashe nebo vašeho programu) můžete pomocí metod napsaných v minulém dílu. Proč musíte proměnnou předávanou z bashe exportovat? Bash funguje také jako programovací jazyk (snad všechny systémové skripty jsou napsány v něm). Jak určitě víte, programovací jazyky používají proměnné k uchovávání hodnot a bylo by krajně nepraktické ukládat proměnné do prostředí bashe, dokonce by mohly vznikat kolapsy se spouštěnými programy. Proto se proměnné ukládají interně v bashi, a pokud je chcete použít v prostředí, musíte je exportovat.

Vytváření dočasných souborů

Občas je potřeba zapsat data do dočasného (temporary) souboru, přečíst je nebo pomocí tohoto souboru předat data jinému procesu. V Linuxu se dočasné soubory ukládají v adresáři /tmp. Měli byste si však dát při používání dočasných souborů pozor na několik záludností:

  • V první řadě mějte na paměti, že na počítači může běžet v jeden moment několik instancí vašeho programu. Pokud budete používat neměnný název dočasného souboru, všechny instance tohoto programu budou zapisovat jen do jednoho souboru. A může se stát, že budou číst data, která nejsou jejich. Každá instance by měla používat jiný název dočasného souboru.
  • Práva souboru by měla být nastavena tak, aby případný útočník nemohl měnit jeho obsah a tím i chod celého programu.
  • Jméno dočasného souboru by mělo být generováno tak, aby nemohlo být externě předpovězeno. Jinak by mohl útočník využít prodlevy mezi testem, zda se již dané jméno používá, a mezi otevřením nového dočasného souboru.

Tyto problémy vám pomohou řešit dvě funkce: 1) Funkce mkstemp(), která je definována v hlavičkovém souboru stdlib.h. Tato funkce využívá deskriptor souboru pro práci se souborem. 2) Funkce tmpfile() je obsažena v hlavičkovém souborustdio.h a využívá pro práci se soubory ukazatel typu FILE. Ve stejném hlavičkovém souboru jsou také definovány funkce jako mktemp(), tempnam() nebo tmpnam(). Tyto funkce slouží také k práci s dočasnými soubory, ale v tomto článku se jimi zabývat nebudu. Zájemce mohu odkázat na manuálové stránky jmenovaných funkcí. Nyní si povíme něco o funkcích mkstemp() a tmpfile().

Funkce mkstemp()

Funkce mkstemp() vytváří dočasný soubor s jedinečným jménem. Toto jméno se generuje pomocí šablony. Funkci se předává jako parametr znakový řetězec obsahující jméno souboru s šablonou. Šablona je „XXXXXX“ a funkce tato X nahradí jinými znaky, aby se vytvořilo jedinečné jméno. Funkce otevře nový soubor s právy číst a zapisovat do souboru pro vlastníka procesu.

Pokud nebudete chtít pomocí dočasného souboru předávat data jinému procesu, pravděpodobně budete chtít soubor zrušit, až nebude potřeba. Soubory vytvořené funkcí mkstemp() se neruší automaticky. Proto použijte po zavolání mkstemp() funkci unlink(), která zruší jméno souboru v souborovém systému. Se souborem bude možné nadále pracovat a po uzavření deskriptoru souboru se odstraní. Snad bych ještě měl upozornit na to, že k zápisu a čtení dat pomocí deskriptoru souboru se používají funkce write() a read(), pokud nevíte, jak se funkce používají, prohlédněte si manuálové stránky, případně se poohlédněte po nějakém článku na Internetu.

V příkladu jsou použity funkce demonstrující použití mkstemp(). První funkce slouží k vytvoření a zápisu dat do dočasného souboru. Vrací deskriptor souboru pro případný další zápis dat a čtení pomocí druhé funkce.

#include <stdlib.h>
#include <unistd.h>

/* Novy datovy typ file_decriptor pro deskriptor souboru */
typedef int file_descriptor;

/* Funkce zapise LENGTH bytu z BUFFER do docasneho souboru.
 * Pote je jmeno souboru zruseno v filesystemu(unlink).
 * Vraci deskriptor souboru. */

file_descriptor write_tmp(char *buffer, size_t length)
{
  /* Vytvori nazev souboru a samotny soubor. XXXXXX bude
   * prepsano nejakymi znaky, aby bylo jmeno unikatni. */
  char temp_filename[] = "/tmp/temp_file.XXXXXX";
  int fd = mkstemp(temp_filename);
  /* Zrusi jmeno souboru v filesystemu, az se
   * uzavre deskriptor souboru soubor se smaze. */
  unlink(temp_filename);
  /* Zapise velikost zapisovanych dat v bytech. */
  write(fd, &length, sizeof(length));
  /* Nyni zapise samotna data. */
  write(fd, buffer, length);
  /* Vrati deskriptor souboru docasneho souboru */
  return(fd);
}

/* Precte data z docasneho souboru, jehoz deskriptor
 * souboru je ji predan jako 1. parametr(TEMP_FILE).
 * Vraci nove alokovany buffer s prectenymi daty, po
 * pouziti musi byt manualne dealokovany pomoci free().
 * Pomoci *LENGTH se vraci velikost bufferu v bytech.
 * Funkce nakonec uzavre deskriptor souboru a tim se
 * pri ukonceni souboru odstrani docasny soubor. */

char *read_tmp(file_descriptor temp_file, size_t *length)
{
  char *buffer;
  /* Vrati se na zacatek souboru. */
  lseek(temp_file, 0, SEEK_SET);
  /* Precte velikost zapsanych dat v souboru. */
  read(temp_file, length, sizeof(*length));
  /* Alokuje pamet pro buffer a cte data. */
  buffer = (char *) malloc (*length);
  read(temp_file, buffer, *length);
  /* Zavre deskriptor souboru */
  close(temp_file);
  return(buffer);
} 

Funkce tmpfile()

Pokud budete chtít použít k zápisu a čtení dat knihovní funkce fwrite() a fread(), můžete použít funkci tmpfile(). Funkce vygeneruje unikátní jméno a použije cestu uloženou v symbolické konstantě P_tmpdir definované vstdio.h. Soubor je otevřen v binárním módu pro čtení i zápis (w+b). Dočasný soubor je již odpojen, jako v předcházejícím příkladě, proto je automaticky odstraněn, jakmile se soubor uzavře, nebo po skončení programu. Následující příklad je pouze přepsaným příkladem funkce mkstemp():

#include <stdio.h>

FILE *write_tmp(char *buffer, size_t length)
{
  /* Vytvori ukazatel pro praci se souborem */
  FILE *temp_file;

  /* Vytvori novy docasny soubor */
  temp_file = tmpfile();
  fwrite(&length, sizeof(length), 1, temp_file);
  fwrite(buffer, length, 1, temp_file);
  return(temp_file);
}

char *read_tmp(FILE *temp_file, size_t *length)
{
  char *buffer;
  fseek(temp_file, 0, SEEK_SET);
  fread(length, sizeof(*length), 1, temp_file);
  buffer = (char *) malloc(*length);
  fread(buffer, *length, 1, temp_file);
  fclose(temp_file);
  return(buffer);
}

Tak to by bylo pro dnešní díl vše, doufám, že si nových znalostí užijete. Příště se podíváme na knihovny. Nejdříve probereme statické knihovny, a jestli zbyde nějaké místo, nakousneme knihovny dynamické.

Našli jste v článku chybu?