/* Komentarze po polsku do funkcji read_exec wykonał: Marcin Mucha  */
/* Komentarze po polsku od funkcji exec_mmap wykonał: Sławek Poreda */
/*
 *  linux/fs/exec.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 */

/*
 * #!-checking implemented by tytso.
 */
/*
 * Demand-loading implemented 01.12.91 - no need to read anything but
 * the header into memory. The inode of the executable is put into
 * "current->executable", and page faults do the actual loading. Clean.
 *
 * Once more I can proudly say that linux stood up to being changed: it
 * was less than 2 hours work to get demand-loading completely implemented.
 *
 * Demand loading changed July 1993 by Eric Youngdale.   Use mmap instead,
 * current->executable is only used by the procfs.  This allows a dispatch
 * table to check for several different types  of binary formats.  We keep
 * trying until we recognize the file or we run out of supported binary
 * formats.
 */

#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/a.out.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/string.h>
#include <linux/stat.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/user.h>
#include <linux/malloc.h>
#include <linux/binfmts.h>
#include <linux/personality.h>

#include <asm/system.h>
#include <asm/segment.h>
#include <asm/pgtable.h>

#include <linux/config.h>
#ifdef CONFIG_KERNELD
#include <linux/kerneld.h>
#endif

asmlinkage int sys_exit(int exit_code);
asmlinkage int sys_brk(unsigned long);

/*
 * Here are the actual binaries that will be accepted:
 * add more with "register_binfmt()" if using modules...
 *
 * These are defined again for the 'real' modules if you are using a
 * module definition for these routines.
 */

/* Lista formatów plików wykonywalnych*/
static struct linux_binfmt *formats = (struct linux_binfmt *) NULL;

void binfmt_setup(void)
/* Inicjalizacja listy formats, funkcje init_*_binfmt() są zdefiniowane
w odpowiednich plikach binfmt_*.c. Ich działanie sprowadza się do
wstawienia odpowiedniej struktury do listy przez wywolanie register_binfmt()
Tylko format script musi być zarejestrowany*/
{
#ifdef CONFIG_BINFMT_ELF
 init_elf_binfmt();
#endif

#ifdef CONFIG_BINFMT_AOUT
 init_aout_binfmt();
#endif

#ifdef CONFIG_BINFMT_JAVA
 init_java_binfmt();
#endif
 /* This cannot be configured out of the kernel */
 init_script_binfmt();
}

int register_binfmt(struct linux_binfmt * fmt)
/* Rejestrowanie formatu, czyli wstawianie struktury fmt do listy formats.
W tej i następnej procedurze należy zwrócić uwagę na dość niestandardową
metodę wstawiania i usuwania elementu z listy jednokierunkowej.
A mianowicie w zmiennej tmp trzymamy wskaźnik na wskaźnik na aktualny
element (wskaźnik ten znajduje się w elemencie poprzednim).*/
{
 struct linux_binfmt ** tmp = &formats;

 if (!fmt)
  return -EINVAL;
 if (fmt->next)
  return -EBUSY;
/* Przeglądanie listy formatów. Nie wstawiamy formatu, który już jest w liście.
Może się wydawać, że instrukcja powyżej jest niepotrzebna skoro i tak
przechodząc listę sprawdzamy, czy formatu tam jeszcze nie ma. Moim zdaniem
sluży ona optymalizacji.*/
 while (*tmp) {
  if (fmt == *tmp)
   return -EBUSY;
  tmp = &(*tmp)->next;
 }
/* Dołączamy format na początku*/
 fmt->next = formats;
 formats = fmt;
 return 0;
}

#ifdef CONFIG_MODULES
int unregister_binfmt(struct linux_binfmt * fmt)
/* Usuwanie formatu z listy formats*/
{
 struct linux_binfmt ** tmp = &formats;

 while (*tmp) {
  if (fmt == *tmp) {
/* Trickowy fragment: w tym przypisaniu zmieniamy pole next poprzednika
aktualnego elementu lub zmienną formats, jeśli jest to pierwszy element.*/
   *tmp = fmt->next;
   return 0;
  }
  tmp = &(*tmp)->next;
 }
/* Nie ma takiego formatu*/
 return -EINVAL;
}
#endif /* CONFIG_MODULES */

int open_inode(struct inode * inode, int mode)
/* Dla danego i-węzła szukamy wolnego miejsca w tablicy plików i tablicy
deskryptorów plików i otwieramy go.*/
{
 int fd;

 if (!inode->i_op || !inode->i_op->default_file_ops)
  return -EINVAL;
/* Szukamy wolnego miejsca w tablicy deskryptorów plików */
 fd = get_unused_fd();
 if (fd >= 0) {
/* A teraz wolnego miejsca w tablicy plików*/
  struct file * f = get_empty_filp();
/* Jeśli się nie udało, zwalniamy miejsce w tablicy deskryptorów
ENFILE - File table overflow */
  if (!f) {
   put_unused_fd(fd);
   return -ENFILE;
  }
/* Inicjalizacja struktury file. Dla pól f_flags i f_mode zdefiniowane są dwa
średnio kompatybilne zestawy stałych. A dokładniej: O_ACCMODE = 3,
O_RDONLY = 0, O_WTONLY = 1, O_RDWT = 2, FMODE_READ = 1, FMODE_WRITE = 2
Poniżej następuje konwersja ze stałych O_* do FMODE_*. */
  f->f_flags = mode;
  f->f_mode = (mode+1) & O_ACCMODE;
  f->f_inode = inode;
  f->f_pos = 0;
  f->f_reada = 0;
  f->f_op = inode->i_op->default_file_ops;
  if (f->f_op->open) {
   int error = f->f_op->open(inode,f);
   if (error) {
/* Nie udało się otworzyć, zwalniamy miejsce w tablicy deskryptorów */
    f->f_count--;
    put_unused_fd(fd);
    return error;
   }
  }
  current->files->fd[fd] = f;
  inode->i_count++;
 }
 return fd;
}

/*
 * Note that a shared library must be both readable and executable due to
 * security reasons.
 *
 * Also note that we take the address to load from from the file itself.
 */
asmlinkage int sys_uselib(const char * library)
/* Funkcja ładująca bibliotekę dzieloną (shared library). Przegląda listę
formats w poszukiwaniu funkcji ładującej. */
{
 int fd, retval;
 struct file * file;
 struct linux_binfmt * fmt;

/* Najpierw otwieramy plik biblioteki. W wypadku niepowodzenia zwracamy bląd */
 fd = sys_open(library, 0, 0);
 if (fd < 0)
  return fd;
 file = current->files->fd[fd];
 retval = -ENOEXEC;
/* Jeśli nie da sie czytać zwracamy ENOEXEC - nie plik wykonywalny */
 if (file && file->f_inode && file->f_op && file->f_op->read) {
/* Przeglądanie listy formats w poszukiwaniu pasującej funkcji load_shlib() */
  for (fmt = formats ; fmt ; fmt = fmt->next) {
   int (*fn)(int) = fmt->load_shlib;
   if (!fn)
    continue;
/* Wywołanie aktualnego load_shlib() */
   retval = fn(fd);
/* Znaleźliśmy format biblioteki, kończymy pętlę przeszukującą formats*/
   if (retval != -ENOEXEC)
    break;
  }
 }
 sys_close(fd);
   return retval;
}

/*
 * count() counts the number of arguments/envelopes
 *
 * We also do some limited EFAULT checking: this isn't complete, but
 * it does cover most cases. I'll have to do this correctly some day..
 */
static int count(char ** argv)
/* Funkcja zlicza napisy w tablicy argv ( jest używana do zliczania
argumentów/zmiennych środowiska) i sprawdza prawa do czytania z obszaru
pamięci w którym się znajdują (chodzi tu o sytuację w której proces
wywołując exec() podaje argv poza dostepną dla niego pamięcią).
Sprawdzanie jest bardzo niekompletne, np. zakłada się, że jeśli proces
może czytać z adresu argv, to może też z argv+1.*/
{
 int error, i = 0;
 char ** tmp, *p;

 if ((tmp = argv) != NULL) {
/* Czy można czytać z argv */
  error = verify_area(VERIFY_READ, tmp, sizeof(char *));
  if (error)
   return error;
  while ((p = get_user(tmp++)) != NULL) {
   i++;
/* Czy możemy czytać z adresow argv[i] (tam są napisy) */
   error = verify_area(VERIFY_READ, p, 1);
   if (error)
    return error;
  }
 }
 return i;
}

/*
 * 'copy_string()' copies argument/envelope strings from user
 * memory to free pages in kernel mem. These are in a format ready
 * to be put directly into the top of new user memory.
 *
 * Modified by TYT, 11/24/91 to add the from_kmem argument, which specifies
 * whether the string and the string array are from user or kernel segments:
 *
 * from_kmem     argv *        argv **
 *    0          user space    user space
 *    1          kernel space  user space
 *    2          kernel space  kernel space
 *
 * We do this by playing games with the fs segment register.  Since it
 * is expensive to load a segment register, we try to avoid calling
 * set_fs() unless we absolutely have to.
 */
unsigned long copy_strings(int argc,char ** argv,unsigned long *page,
  unsigned long p, int from_kmem)
/* Funkcja ta kopiuje argumenty/zmienne środowiska do pamięci jądra.
Miejsce z którego są kopiowane (pamięć jądra/procesu) określa from_kmem.
page jest tablicą stron, a p najwyższym wolnym adresem na tych stronach.
W razie potrzeby dodatkowe strony są dodawane do tablicy.
UWAGI:
(1) oznacza to, że nie wszystkie page[i] są adresami istniejących stron,
z reguły tylko kilka ostatnich. Najwyższe i, dla którego page[i] jest
zarezerwowaną stroną to oczywiscie p/PAGE_SIZE.
(2) Jeśli czytamy z pamięci jądra musimy ustawić rejestr fs na ds, do tego
służą występujące w kodzie instrukcje get_ds(), get_fs(), set_fs().*/

{
 char *tmp, *tmp1, *pag = NULL;
 int len, offset = 0;
 unsigned long old_fs, new_fs;

 if (!p)
  return 0; /* bullet-proofing */
 new_fs = get_ds();
 old_fs = get_fs();
 if (from_kmem==2)
  set_fs(new_fs);
 while (argc-- > 0) {
  if (from_kmem == 1)
   set_fs(new_fs);
/* Pobieramy adres kolejnego (od końca) napisu */
  if (!(tmp1 = tmp = get_user(argv+argc)))
   panic("VFS: argc is wrong");
  if (from_kmem == 1)
   set_fs(old_fs);
/* Obliczamy długość napisu */
  while (get_user(tmp++));
  len = tmp - tmp1;
/* Nie ma miejsca na skopiowanie napisu - patrz poniżej */
  if (p < len) { /* this shouldn't happen - 128kB */
   set_fs(old_fs);
   return 0;
  }
/* Pętla - bo jeśli napis wypada na granicy stron, to będzie kopiowany w dwóch
rundach ( a nawet więcej, choć to chyba rzadkość) */
  while (len) {
/* Kopiujemy co najmniej 1 znak */
   --p; --tmp; --len;
/* Czy przekraczamy granicę strony */
   if (--offset < 0) {
    offset = p % PAGE_SIZE;
    if (from_kmem==2)
     set_fs(old_fs);
/* Jeśli nie udało się przydzielić strony to niedobrze - zwracamy 0 */
    if (!(pag = (char *) page[p/PAGE_SIZE]) &&
        !(pag = (char *) page[p/PAGE_SIZE] =
          (unsigned long *) get_free_page(GFP_USER)))
     return 0;
    if (from_kmem==2)
     set_fs(new_fs);

   }
/* Jeśli był tylko 1 znak, to go przepisujemy */
   if (len == 0 || offset == 0)
     *(pag + offset) = get_user(tmp);
/* wpp. kopiujemy 1 + min(offset,len) czyli maksymalnie ile mieści się na
stronie */
   else {
     int bytes_to_copy = (len > offset) ? offset : len;
     tmp -= bytes_to_copy;
     p -= bytes_to_copy;
     offset -= bytes_to_copy;
     len -= bytes_to_copy;
     memcpy_fromfs(pag + offset, tmp, bytes_to_copy + 1);
   }
  }
 }
 if (from_kmem==2)
  set_fs(old_fs);
 return p;
}

unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm)
/* Funkcja alokuje strukturę vm_area_struct dla procesu, włącza ją do struktury
task_struct i podłącza do niej strony z argumentami i zmiennymi środowiska.
Jeśli zabraknie pamięci na tę strukturę, zwalnia strony z argumentami i
zmiennymi środowiska.*/
{
 unsigned long stack_base;
 struct vm_area_struct *mpnt;
 int i;

/* Obliczamy adres dla argumentów i zmiennych środowiska
Umieszczamy je pod końcowymi adresami*/
 stack_base = STACK_TOP - MAX_ARG_PAGES*PAGE_SIZE;
 p += stack_base;
 if (bprm->loader)
  bprm->loader += stack_base;
 bprm->exec += stack_base;
/* Przydzielamy strukturę vm_are_struct i wypełniamy ją */
 mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL);
 if (mpnt) {
  mpnt->vm_mm = current->mm;
  mpnt->vm_start = PAGE_MASK & (unsigned long) p;
  mpnt->vm_end = STACK_TOP;
  mpnt->vm_page_prot = PAGE_COPY;
  mpnt->vm_flags = VM_STACK_FLAGS;
  mpnt->vm_ops = NULL;
  mpnt->vm_offset = 0;
  mpnt->vm_inode = NULL;
  mpnt->vm_pte = 0;
/* Dodajemy segment do pamięci procesu */
  insert_vm_struct(current->mm, mpnt);
  current->mm->total_vm = (mpnt->vm_end - mpnt->vm_start) >> PAGE_SHIFT;

/* Umieszczamy strony z page w przestrzeni adresowej procesu */
  for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
   if (bprm->page[i]) {
    current->mm->rss++;
    put_dirty_page(current,bprm->page[i],stack_base);
   }
   stack_base += PAGE_SIZE;
  }
 } else {
  /*
   * This one is tricky. We are already in the new context, so we cannot
   * return with -ENOMEM. So we _have_ to deallocate argument pages here,
   * if there is no VMA, they wont be freed at exit_mmap() -> memory leak.
   *
   * User space then gets a SIGSEGV when it tries to access argument pages.
   */
/* Jeśli nie ma pamięci na vm_area_struct, zwalniamy strony page, patrz wyżej */
  for (i = 0 ; i < MAX_ARG_PAGES ; i++) {
   if (bprm->page[i]) {
    free_page(bprm->page[i]);
    bprm->page[i] = 0;
   }
  }
 }

 return p;
}

/*
 * Read in the complete executable. This is used for "-N" files
 * that aren't on a block boundary, and for files on filesystems
 * without bmap support.
 */
int read_exec(struct inode *inode, unsigned long offset,
 char * addr, unsigned long count, int to_kmem)
/* Funkcja wczytuje plik wykonywalny o danym i-węźle od ustalonego offset-u pod
podany adres w pamięci jądra lub użytkownika. Sprawdza przy tym większość
możliwych błędów */
{
 struct file file;
 int result = -ENOEXEC;

 if (!inode->i_op || !inode->i_op->default_file_ops)
  goto end_readexec;
 file.f_mode = 1; // FMODE_READ
 file.f_flags = 0; // O_RDONLY
 file.f_count = 1;
 file.f_inode = inode;
 file.f_pos = 0;
 file.f_reada = 0;
 file.f_op = inode->i_op->default_file_ops;
/* Czy można otworzyć */
 if (file.f_op->open)
  if (file.f_op->open(inode,&file))
   goto end_readexec;
/* Czy można czytać */
 if (!file.f_op || !file.f_op->read)
  goto close_readexec;
/* Ustawiamy plik na odpowiedni offset - możliwy błąd */
 if (file.f_op->lseek) {
  if (file.f_op->lseek(inode,&file,offset,0) != offset)
    goto close_readexec;
 } else
  file.f_pos = offset;
/* Jeśli to_kmem<>0 to wczytujemy plik do pamięci jądra pod adres addr
Sztuczki z rejestrem fs jak w copy_strings() */
 if (to_kmem) {
  unsigned long old_fs = get_fs();
  set_fs(get_ds());
  result = file.f_op->read(inode, &file, addr, count);
  set_fs(old_fs);
/* W przeciwnym przypadku musimy wcześniej sprawdzić, czy proces ma prawo
do pisania pod adresem addr */
 } else {
  result = verify_area(VERIFY_WRITE, addr, count);
  if (result)
   goto close_readexec;
  result = file.f_op->read(inode, &file, addr, count);
 }
close_readexec:
 if (file.f_op->release)
  file.f_op->release(inode,&file);
end_readexec:
 return result;
}

static int exec_mmap(void)
{
  /*
   * The clear_page_tables done later on exec does the right thing
   * to the page directory when shared, except for graceful abort
   */
  /* jeśli proces współdzieli z innym  pole mm to dokonaj rozdzielenia */
  if (current->mm->count > 1) {
    struct mm_struct *old_mm, *mm = kmalloc(sizeof(*mm), GFP_KERNEL); /* zaalokuj pamięć */
    if (!mm)
      return -ENOMEM;

    *mm = *current->mm;  /* skopiuj obecną zawartość */
    mm->def_flags = 0;   /* should future lockings be kept? */
    mm->count = 1;       /* ustaw ilość odwołań do struktury na 1 */
    mm->mmap = NULL;     /* wyzeruj listę obszarów pamięci zajętych przez proces */
    mm->mmap_avl = NULL; /* wyzeruj drzewo AVL obszarów pamięci zajętych przez proces */
    mm->total_vm = 0;    /* ustaw ilość zajętej pamięci na zero */
    mm->rss = 0;         /* ustaw ilość ramek zajętych przez proces na zero */

    old_mm = current->mm;
    current->mm = mm;
    new_page_tables(current);

    if ((old_mm != &init_mm) && (!--old_mm->count)) {
      /*
       * all threads exited while we were sleeping, 'old_mm' is held
       * by us exclusively, lets get rid of it:
       */
      exit_mmap(old_mm);
      free_page_tables(old_mm);      /* zwolnij zajęte strony pamięci */
      kfree(old_mm);                 /* zwolnij pamięć zajętą przez strukturę mm */
    }

    return 0;
  }
  /* usuń struktury pamięci, zajęte przez poprzedni proces */
  flush_cache_mm(current->mm);
  exit_mmap(current->mm);
  clear_page_tables(current);
  flush_tlb_mm(current->mm);

  return 0;
}

/*
 * These functions flushes out all traces of the currently running executable
 * so that a new one can be started
 */

static inline void flush_old_signals(struct signal_struct *sig)
/* usuwa funkcje obsługi przerwań starego procesu */
{
  int i;
  struct sigaction * sa = sig->action;

  for (i=32 ; i != 0 ; i--) {        /* dla każdego z 32 sygnałów */
    sa->sa_mask = 0;                 /* wyzeruj maskę */
    sa->sa_flags = 0;                /* wyzeruj flagi */
    if (sa->sa_handler != SIG_IGN)   /* jeśli ustawione ignorowanie to pozostaw */
      sa->sa_handler = NULL;         /* w przeciwnym wypadku ustaw obsługę domyślną */
    sa++;                            /* przejdą do następnego sygnału */
  }
}

static inline void flush_old_files(struct files_struct * files)
/* zwalnia deskryptory plików, które mają być zamknięte przy wywołaniu exec */
{
  unsigned long j;

  j = 0;
  for (;;) {
    unsigned long set, i;

    i = j * __NFDBITS;                        /* __NFDBITS  jest równe sizeof( fds_bits[j] ) */
    if (i >= NR_OPEN)                         /* już przejrzane wszystkie deskryptory */
      break;
    /* każdy bit odpowiada za deskryptor o kolejnym numerze */
    set = files->close_on_exec.fds_bits[j];
    /* zeruj kolejne __NFDBITS bitów */
    files->close_on_exec.fds_bits[j] = 0;
    /* następna  będzie grupa __NGDBITS bitów z pod adresu fds_bits[j] */
    j++;
    /* przejrzyj kolejne __NFDBITS bitów, zwalniając te deskryptory, */
    for ( ; set ; i++,set >>= 1) {
      if (set & 1)                      /* których bity są ustawione */
        sys_close(i);
    }
  }
}

int flush_old_exec(struct linux_binprm * bprm)
{
  int i;
  int ch;
  char * name;

/* ze względów bezpieczeństwa nie możliwy jest zrzut pamięci,   */
/* w przypadku gdy wykonuje się plik z atrybutem s, gdyż wtedy, */
/* w czasie wykonanie proces posiada uprawnienia administratora */
  if (current->euid == current->uid && current->egid == current->gid)
    current->dumpable = 1;                    /* zaznacz, że można dokonać zrzutu pamięci */
  name = bprm->filename;
  for (i=0; (ch = *(name++)) != '\0';) {      /* wyłuskaj z pełnej ścieżki nazwę programu */
    if (ch == '/')
      i = 0;
    else
      if (i < 15)                             /* i zapamiętaj jej pierwsze 15 znaków */
        current->comm[i++] = ch;
  }
  current->comm[i] = '\0';                    /* zakończ nazwę zerem */

  /* Release all of the old mmap stuff. */
  if (exec_mmap())
    return -ENOMEM;

  flush_thread();                             /* niskopoziomowe zniszczenie procesu */

  if (bprm->e_uid != current->euid || bprm->e_gid != current->egid ||
      permission(bprm->inode,MAY_READ))
    current->dumpable = 0;

  flush_old_signals(current->sig);   /* usuń stare obsługi przerwan */
  flush_old_files(current->files);   /* zamknij pliki które mają być zamknięte przy exec */

  return 0;
}

/*
 * Fill the binprm structure from the inode.
 * Check permissions, then read the first 512 bytes
 */
int prepare_binprm(struct linux_binprm *bprm)
/* ustawia strukturę binprm na podstawie i-węzła programu, sprawdza uprawnienia, */
/* wczytuje pierwsze 128 bajtów pliku */
{
  int mode;
  int retval,id_change;

  mode = bprm->inode->i_mode;
  /* musi to być regularny plik - nie katalog, nie urządzenie */
  if (!S_ISREG(mode))     /* must be regular file */
    return -EACCES;
  /* musi być przynajmniej jeden bit zezwalający na wykonanie */
  if (!(mode & 0111))     /* with at least _one_ execute bit set */
    return -EACCES;
  /* czy system plików nie jest podłączony z parametrem noexec - */
  /* bez możliwości wykonywania jakichkolwiek plików */
  if (IS_NOEXEC(bprm->inode))   /* FS mustn't be mounted noexec */
    return -EACCES;
  if (!bprm->inode->i_sb)
    return -EACCES;
  /* proces musi mieć prawo wykonać ten plik */
  if ((retval = permission(bprm->inode, MAY_EXEC)) != 0)
    return retval;
 /* sprawdą czy przypadkiem ktoś nie zapisuje do tego pliku */
 /* better not execute files which are being written to */
 if (bprm->inode->i_writecount > 0)
    return -ETXTBSY;

  bprm->e_uid = current->euid;
  bprm->e_gid = current->egid;
  id_change = 0;

  /* czy ten plik ma atrybut s - to jest czy proces przed wykonaniem programu, */
  /* zmienić swój efektywny uid na uid adminstratora */
  /* Set-uid? */
  if (mode & S_ISUID) {
    bprm->e_uid = bprm->inode->i_uid;
    if (bprm->e_uid != current->euid)
      id_change = 1;                                  /* tak, należy zmienić euid */
  }

  /* Set-gid? */
  /*
   * If setgid is set but no group execute bit then this
   * is a candidate for mandatory locking, not a setgid
   * executable.
   */
  /* flaga set gid oraz dozwolone wykonywanie dla innych użytkowników tej grupy */
  if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
    bprm->e_gid = bprm->inode->i_gid;
    if (!in_group_p(bprm->e_gid))
      id_change = 1;
  }

  if (id_change) {
    /* We can't suid-execute if we're sharing parts of the executable */
    /* or if we're being traced (or if suid execs are not allowed)    */
    /* (current->mm->count > 1 is ok, as we'll get a new mm anyway)   */
    /* ze względów bezpieczeństwa nie można zmienić uid jeśli: */
    if (IS_NOSUID(bprm->inode)             /* system plików podłączony z parametrem nosuid */
        || (current->flags & PF_PTRACED)   /* proces jest śledzony */
        || (current->fs->count > 1)
        || (current->sig->count > 1)
        || (current->files->count > 1)) {
      if (!suser())      /* jesli nie administrator */
        return -EPERM;
    }
  }

  memset(bprm->buf,0,sizeof(bprm->buf));             /* wyczyść bufor */
  /* wczytaj do bufora pierwsze 128 znaków do analizy formatu programu wykonalnego */
  return read_exec(bprm->inode,0,bprm->buf,128,1);
}

void remove_arg_zero(struct linux_binprm *bprm)
/* usuwa argument zerowy */
{
  if (bprm->argc) {
    unsigned long offset;
    char * page;
    offset = bprm->p % PAGE_SIZE;
    page = (char*)bprm->page[bprm->p/PAGE_SIZE];
    /* przesuwaj się ze zmienną bprm->p, aż natrafisz na koniec pierwszego argumentu */
    while(bprm->p++,*(page+offset++))
      if(offset==PAGE_SIZE){
        offset=0;
        page = (char*)bprm->page[bprm->p/PAGE_SIZE];
      }
    bprm->argc--;
  }
}

/*
 * cycle the list of binary formats handler, until one recognizes the image
 */
int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
/* przegląda listę formatów wykonywalnych, aż znajdzie taki, który odpowiada temu plikowi */
{
  int try,retval=0;
  struct linux_binfmt *fmt;
#ifdef __alpha__
  /* handle /sbin/loader.. */
  {
      struct exec * eh = (struct exec *) bprm->buf;

      if (!bprm->loader && eh->fh.f_magic == 0x183 &&
    (eh->fh.f_flags & 0x3000) == 0x3000)
      {
    char * dynloader[] = { "/sbin/loader" };
    iput(bprm->inode);
    bprm->dont_iput = 1;
    remove_arg_zero(bprm);
    bprm->p = copy_strings(1, dynloader, bprm->page, bprm->p, 2);
    bprm->argc++;
    bprm->loader = bprm->p;
    retval = open_namei(dynloader[0], 0, 0, &bprm->inode, NULL);
    if (retval)
      return retval;
    bprm->dont_iput = 0;
    retval = prepare_binprm(bprm);
    if (retval<0)
      return retval;
    /* should call search_binary_handler recursively here,
       but it does not matter */
      }
  }
#endif
  for (try=0; try<2; try++) {  /* wykonaj dwie próby - dwa obroty tej pętli */
    for (fmt = formats ; fmt ; fmt = fmt->next) {  /* dla każdego zarejestrowanego formatu */
      /* fn wskazuje na funkcję ładującą plik wykonywalny danego formatu */
      int (*fn)(struct linux_binprm *, struct pt_regs *) = fmt->load_binary;
      if (!fn) /* jeśli jej nie ma, to następny format */
        continue;
      retval = fn(bprm, regs);  /* wywołaj funkcję ładującą plik wykonywalny */
      if (retval >= 0) {
        if(!bprm->dont_iput) /* jeśli jeszcze nie zwolniono i-węzła */
          iput(bprm->inode); /* to zrób to */
        bprm->dont_iput=1;       /* zaznacz, że już to zrobiono */
        current->did_exec = 1;   /* zaznacz, że proces próbował wykonać exec */
        return retval;
      }
      if (retval != -ENOEXEC)
        break;
      /* Czy i-węzeł już został zwolniony */
      if (bprm->dont_iput) /* We don't have the inode anymore*/
        return retval;
    }
    if (retval != -ENOEXEC) {
      break;
#ifdef CONFIG_KERNELD
    }else{
      /* czy to znak c jest normalnym znakiem lub separatorem, */
#define printable(c) (((c)=='\t') || ((c)=='\n') || (0x20<=(c) && (c)<=0x7e))
      char modname[20];
      if (printable(bprm->buf[0]) &&
          printable(bprm->buf[1]) &&
          printable(bprm->buf[2]) &&
          printable(bprm->buf[3]))
        break; /* -ENOEXEC */
/* utwórz nazwę modułu, jaki trzeba załadować: binfmt-..., gdzie .. to 4 pierwsze znaki ??? */
      sprintf(modname, "binfmt-%hd", *(short*)(&bprm->buf));
      request_module(modname);  /* zaladuj modul do pamieci */
#endif
    }
  }
  return retval;
}

/*
 * sys_execve() executes a new program.
 */
int do_execve(char * filename, char ** argv, char ** envp, struct pt_regs * regs)
/*
 * wywoływane przez sys_execve() - wykonaj nowy program
 */
{
  struct linux_binprm bprm;
  int retval;
  int i;

  bprm.p = PAGE_SIZE*MAX_ARG_PAGES-sizeof(void *);
  /* zeruj tablicę stron */
  for (i=0 ; i<MAX_ARG_PAGES ; i++) /* clear page-table */
    bprm.page[i] = 0;
  retval = open_namei(filename, 0, 0, &bprm.inode, NULL);
  if (retval)
    return retval;
  bprm.filename = filename;        /* ustaw nazwę pliku */
  bprm.sh_bang = 0;                /* znacznik używany, aby zapobiec sytuacji, */
                                   /* że interpretator skryptu, sam jest skryptem */
  bprm.loader = 0;
  bprm.exec = 0;
  bprm.dont_iput = 0;
  if ((bprm.argc = count(argv)) < 0)      /* policz i zapamiętaj ilość argumentów */
    return bprm.argc;
  if ((bprm.envc = count(envp)) < 0)      /* policz i zapamiętaj ilość elementów środowiska */
    return bprm.envc;

  retval = prepare_binprm(&bprm);         /* sprawdzenia i ustawienie dalszych parametrów */
 
  /* jeśli w porządku, to przenieś elementy miedzy przestrzeniami adresowymi */
  if(retval>=0) {

    /* przenieś nazwę pliku */
    bprm.p = copy_strings(1, &bprm.filename, bprm.page, bprm.p, 2);
    bprm.exec = bprm.p;
    /* przenieś środowisko programu */
    bprm.p = copy_strings(bprm.envc,envp,bprm.page,bprm.p,0);
    /* przenieś argumenty programu */
    bprm.p = copy_strings(bprm.argc,argv,bprm.page,bprm.p,0);
    if (!bprm.p)
      retval = -E2BIG;
  }

  if(retval>=0)
    /* znajdą odpowiedni "loader", który obsługuje ten format */
    retval = search_binary_handler(&bprm,regs);
  if(retval>=0)
    /* wywołanie execve udało się */
    /* execve success */
    return retval;

  /* Zwróć i-węzeł */
  /* Something went wrong, return the inode and free the argument pages*/
  if(!bprm.dont_iput)
    iput(bprm.inode);
  /* zwolnij strony pamięci, które były zarezerwowane dla argumentów */
  for (i=0 ; i<MAX_ARG_PAGES ; i++)
    free_page(bprm.page[i]);
  return(retval);
}