/* Komentarze po polsku wykonał: Sławek Poreda */
#include <linux/personality.h>
#include <linux/ptrace.h>
#include <linux/sched.h>
#include <linux/mm.h>

static asmlinkage void no_lcall7(struct pt_regs * regs);

/* tożsamościowe mapowanie numerów sygnałów . Inne domeny wykonywania mogą mieć inne. */
static unsigned long ident_map[32] = {
  0,  1,  2,  3,  4,  5,  6,  7,
  8,  9,  10, 11, 12, 13, 14, 15,
  16, 17, 18, 19, 20, 21, 22, 23,
  24, 25, 26, 27, 28, 29, 30, 31
};

struct exec_domain default_exec_domain = {
  /* nazwa - standardowa domena wykonania linuxa*/
  "Linux",  /* name */
  /* lcall7 powoduje błąd segmentacji */
  no_lcall7,  /* lcall7 causes a seg fault. */
  /* obsługuje pełny zakres osobowości (personalities) */
  0, 0xff,  /* All personalities. */
  /* mapowanie ze standadowych numerów sygnałów w Linuxie */
  /* na charakterystyczne dla domeny wykonywania */
  ident_map,  /* Identity map signals. */
  /* i na odwrót - w przypadku domeny Linux - oba te mapowania są identycznościowe */
  ident_map,  /*  - both ways. */
  /* licznik wykorzystania - ile procesów korzysta z tej domeny, NULL - nie zliczamy tego */
  NULL,   /* No usage counter. */
  /* następny element na liście domen */
  NULL    /* Nothing after this in the list. */
};

/* lista dostępnych w systemie domen wykonywania */
static struct exec_domain *exec_domains = &default_exec_domain;
 

static asmlinkage void no_lcall7(struct pt_regs * regs)
{
  /*
   * This may have been a static linked SVr4 binary, so we would have the
   * personality set incorrectly.  Check to see whether SVr4 is available,
   * and use it, otherwise give the user a SEGV.
   */

  /* jeśli to jest kod SVr4 zlinkowany statycznie, to mógł zajść przypadek, */
  /* że osobowość(personality) była ustawiona niepoprawnie.                 */
  /* Sprawdź, czy jest dostępna domena dla SVr4 i jeśli tak użyj jej,       */
  /* jeśli nie, to wywołaj błąd segmentacji */

  /* zmieniamy domenę wykonywania bieżącego procesu, */
  /* jeśli był licznik odwołań do struktury tej domeny jest używany to go zmniejsz */
  if (current->exec_domain && current->exec_domain->use_count)
    (*current->exec_domain->use_count)--;

  /* zmień osobowość na SVr4 */
  current->personality = PER_SVR4;

  /* spróbuj znaleźć domenę która to obsługuję */
  current->exec_domain = lookup_exec_domain(current->personality);

  /* jeśli trzeba zwiększ licznik odwołań do niej */
  if (current->exec_domain && current->exec_domain->use_count)
    (*current->exec_domain->use_count)++;

  /* jeśli jest procedura obsługi różna od tej - czyli znaleziono inną domenę */
  /* niż standardowa Linuxa - to wywołaj tę procedurę                         */
  if (current->exec_domain && current->exec_domain->handler
  && current->exec_domain->handler != no_lcall7) {
    current->exec_domain->handler(regs);
    return;
  }

  /* w przeciwnym razie błąd segmentacji */
  send_sig(SIGSEGV, current, 1);
}
 

struct exec_domain *lookup_exec_domain(unsigned long personality)
{
  unsigned long pers = personality & PER_MASK;
  struct exec_domain *it;

  /* przejrzyj listę domen i znajdź pierwszą do której zakresu należy ta osobowość */
  for (it=exec_domains; it; it=it->next)
    if (pers >= it->pers_low
    && pers <= it->pers_high)
      return it;

  /* Tu nigdy nie dojdzie gdyż na końcu listy jest standardowa domena Linuxa, */
  /* a ona obsługuje wszystkie osobowości */
  /* Should never get this far. */
  printk(KERN_ERR "No execution domain for personality 0x%02lx\n", pers);
  return NULL;
}

/* zarejestruj nową domenę wykonywania */
int register_exec_domain(struct exec_domain *it)
{
  struct exec_domain *tmp;

  /* czy różna od NULL */
  if (!it)
    return -EINVAL;
  /* czy nie tworzy jakiejś listy */
  if (it->next)
    return -EBUSY;
  /* czy nie ma jej na liście domen */
  for (tmp=exec_domains; tmp; tmp=tmp->next)
    if (tmp == it)
      return -EBUSY;
  /* jeśli wszystko ok. to dodaj nową domenę na początku listy */
  it->next = exec_domains;
  exec_domains = it;
  return 0;
}

/* wyrejestruj domenę wykonywania */
int unregister_exec_domain(struct exec_domain *it)
{
  struct exec_domain ** tmp;

  tmp = &exec_domains;
  /* znajdź tę domenę na liście domen i usuń ją */
  while (*tmp) {
    if (it == *tmp) {
      *tmp = it->next;
      it->next = NULL;
      return 0;
    }
    tmp = &(*tmp)->next;
  }
  return -EINVAL;
}

asmlinkage int sys_personality(unsigned long personality)
{
  struct exec_domain *it;
  unsigned long old_personality;

  if (personality == 0xffffffff)
    return current->personality;

  /* znajdź domenę wykonywania rozpoznającą to osobowość */
  it = lookup_exec_domain(personality);
  if (!it)
    return -EINVAL;

  /* zapamiętaj poprzednią osobowość procesu */
  old_personality = current->personality;
  /* zmniejsz liczbę odwołań do domeny */
  if (current->exec_domain && current->exec_domain->use_count)
    (*current->exec_domain->use_count)--;
  /* ustaw nową osobowość */
  current->personality = personality;
  /* ustaw nową domenę wykonywania */
  current->exec_domain = it;
  /* zwiększ licznik odwołań do niej */
  if (current->exec_domain->use_count)
    (*current->exec_domain->use_count)++;

  /* zwróć poprzednią osobowość */
  return old_personality;
}